Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/hjarni-com:hjarni-com-tags-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Hjarni REST Tags API
version: 1.0.0
description: Hjarni is a Markdown knowledge base that AI assistants and people share; this API reads and writes the same notes, folders (called containers) and tags that its MCP server exposes. Requests go to https://hjarni.com/api/v1 with a bearer token in the Authorization header, send and receive JSON, and list endpoints page with `page` and `per_page` and report totals in the `X-Total-Count`, `X-Page` and `X-Per-Page` headers. Requests are limited to 100 a minute per account across all of its tokens, and going over answers 429 with a `Retry-After` header.
termsOfService: https://hjarni.com/terms
contact:
name: Hjarni
email: evert@hjarni.com
url: https://hjarni.com/docs/api
servers:
- url: https://hjarni.com/api/v1
security:
- bearerAuth: []
tags:
- name: Tags
description: Tags that label notes across folders.
paths:
/tags:
get:
operationId: listTags
summary: List tags
description: Lists the person's tags by name, each with the number of non-trashed notes carrying it. An agent token sees only tags on notes it can read, counted over those notes. Paginated.
tags:
- Tags
parameters:
- $ref: '#/components/parameters/Page'
- $ref: '#/components/parameters/PerPage'
responses:
'200':
description: One page of tags.
headers:
X-Total-Count:
$ref: '#/components/headers/X-Total-Count'
X-Page:
$ref: '#/components/headers/X-Page'
X-Per-Page:
$ref: '#/components/headers/X-Per-Page'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Tag'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
operationId: createTag
summary: Create a tag
description: Creates a tag and returns it with 201. Names are unique per person (422 for a duplicate). To tag a note, set the note's `tag_list` instead, which also creates missing tags. Agent tokens are refused with 403.
tags:
- Tags
requestBody:
$ref: '#/components/requestBodies/TagCreate'
responses:
'201':
description: The created tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'422':
$ref: '#/components/responses/UnprocessableEntity'
'429':
$ref: '#/components/responses/TooManyRequests'
/tags/{id}:
parameters:
- $ref: '#/components/parameters/TagId'
get:
operationId: getTag
summary: Get a tag
description: Returns one tag with its note count. An agent token can read only tags on notes it can read; others answer 404.
tags:
- Tags
responses:
'200':
description: The tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
patch:
operationId: updateTag
summary: Rename a tag
description: Renames the tag everywhere it is used and returns it. Agent tokens are refused with 403.
tags:
- Tags
requestBody:
$ref: '#/components/requestBodies/TagUpdate'
responses:
'200':
description: The renamed tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
$ref: '#/components/responses/UnprocessableEntity'
'429':
$ref: '#/components/responses/TooManyRequests'
put:
operationId: updateTagWithPut
summary: Rename a tag (PUT)
description: The same action as PATCH /tags/{id}. Prefer PATCH.
tags:
- Tags
requestBody:
$ref: '#/components/requestBodies/TagUpdate'
responses:
'200':
description: The renamed tag.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
$ref: '#/components/responses/UnprocessableEntity'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
operationId: deleteTag
summary: Delete a tag
description: Deletes the tag and answers 204. Notes keep their content; only the tag is removed from them. This cannot be undone. Agent tokens are refused with 403.
tags:
- Tags
responses:
'204':
description: The tag is deleted.
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
/tags/{id}/merge:
parameters:
- $ref: '#/components/parameters/TagId'
patch:
operationId: mergeTag
summary: Merge a tag into another
description: Moves every note carrying this tag onto `target_id`, deletes this tag, and returns the target tag. Merging a tag into itself is not handled and fails with a 500. Agent tokens are refused with 403.
tags:
- Tags
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- target_id
properties:
target_id:
type: integer
description: The tag that survives. Missing or unknown answers 404.
example:
target_id: 5
responses:
'200':
description: The target tag, after the merge.
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
/tags/{id}/notes:
parameters:
- $ref: '#/components/parameters/TagId'
get:
operationId: listTagNotes
summary: List a tag's notes
description: Lists the active notes carrying this tag, with bodies, in the same order as GET /notes. An agent token sees only the notes it can read. Paginated.
tags:
- Tags
parameters:
- $ref: '#/components/parameters/Page'
- $ref: '#/components/parameters/PerPage'
responses:
'200':
description: One page of notes with this tag.
headers:
X-Total-Count:
$ref: '#/components/headers/X-Total-Count'
X-Page:
$ref: '#/components/headers/X-Page'
X-Per-Page:
$ref: '#/components/headers/X-Per-Page'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/NoteSummary'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
components:
responses:
UnprocessableEntity:
description: The request was understood but not applied. Model validations answer with an `errors` array of messages; parameter checks (an unknown note key, a surgical edit whose anchor does not match, a missing file `url`, `ordered_ids` that do not match the folders in scope, a URL that could not be fetched) answer with an `errors` array or a single `error` string, as noted on each endpoint.
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/ValidationErrors'
- $ref: '#/components/schemas/Error'
examples:
validation:
summary: Model validation
value:
errors:
- Title can't be blank
parameterCheck:
summary: Parameter check
value:
error: ordered_ids must contain exactly all containers in this scope
NotFound:
description: No record with that id that this token can reach (including notes and folders in the Trash, except where an endpoint says otherwise), or no endpoint for this method and path. A revision that does not exist answers with `error` only.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
status: 404
error: Not Found
code: not_found
message: No endpoint matches this method and path, or the record is not one this token can reach. Every endpoint is listed in https://hjarni.com/openapi.json.
docs_url: https://hjarni.com/docs/api#errors
Forbidden:
description: 'Refused, for one of three reasons. (1) A plan limit: the Free plan''s note limit on create or restore, the Free file allowance on an attachment, or an account that is read-only because it holds more notes than its plan carries. The body has `error` and an `upgrade_url`, and the personal note-limit refusal also a `team_url`. (2) An agent token outside its grants: reading or writing where it was not given access, writing where it only reads, or calling an endpoint agent tokens cannot use. The body has `error` "Forbidden: outside token scope" with `code` `out_of_scope`, `message` and `docs_url`, and no `upgrade_url`. (3) A team folder where the person''s role is below editor: `error` only, "You don''t have permission to do that (editor required)."'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
outOfScope:
summary: Agent token outside its grants
value:
error: 'Forbidden: outside token scope'
code: out_of_scope
message: This token cannot do this. An agent token reaches only the folders it was given, at the level it was given, and never manages tags, personal instructions or folder order; those need a full-access token. A token's access is set in Settings > Connections.
docs_url: https://hjarni.com/docs/agent-tokens
noteLimit:
summary: Free plan note limit reached
value:
error: You've reached the 25 personal note limit on the Free plan. Upgrade to Pro for unlimited notes, or start a shared team knowledge base if this should live with your teammates.
upgrade_url: /checkout/new?reason=note_limit&ref=paywall_note_limit&t=CHECKOUT_TOKEN
team_url: /teams/new?ref=paywall_note_limit
editorRequired:
summary: Team role below editor
value:
error: You don't have permission to do that (editor required).
TooManyRequests:
description: Over the rate limit of 100 requests a minute per account, counted across all of its tokens (counters are kept per server process, so the ceiling actually enforced can be somewhat higher). Wait the number of seconds in `Retry-After`. The body has `error` only.
headers:
Retry-After:
$ref: '#/components/headers/Retry-After'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Rate limit exceeded. Retry after 12 seconds.
BadRequest:
description: The body could not be read (malformed JSON), or a required wrapper or parameter such as `note`, `container`, `tag` or `ordered_ids` is missing or empty.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
status: 400
error: Bad Request
code: bad_request
message: The ordered_ids parameter is missing or empty. Each endpoint's request body is in https://hjarni.com/openapi.json.
docs_url: https://hjarni.com/docs/api#errors
Unauthorized:
description: The bearer token is missing, unknown or expired.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Unauthorized
code: unauthorized
message: 'Missing or invalid token. Send it as Authorization: Bearer <token>; tokens are created in Settings > Connections.'
docs_url: https://hjarni.com/docs/api#auth
parameters:
Page:
name: page
in: query
description: Page number, starting at 1. Values below 1 are treated as 1.
schema:
type: integer
minimum: 1
default: 1
TagId:
name: id
in: path
required: true
description: The tag's id.
schema:
type: integer
PerPage:
name: per_page
in: query
description: Items per page. Values outside 1-100 are clamped.
schema:
type: integer
minimum: 1
maximum: 100
default: 25
schemas:
TeamRef:
type: object
required:
- id
- name
properties:
id:
type: integer
name:
type:
- string
- 'null'
NoteSummary:
type: object
description: A note as list endpoints return it. `body` is left out when the request sets `exclude_body=true`; `last_edited_by` and `last_edited_by_agent` appear only once someone has edited the note; `team` appears only on team notes.
required:
- id
- title
- summary
- summary_stale
- source_url
- archived
- favorited
- position
- lock_version
- created_at
- updated_at
- tag_list
- tags
- container
- files
properties:
id:
type: integer
title:
type: string
body:
type:
- string
- 'null'
description: Markdown. `[[id:Title]]` is a wiki-link to another note.
summary:
type:
- string
- 'null'
description: A short summary for scanning.
summary_stale:
type: boolean
description: True when the body changed after the summary was written.
source_url:
type:
- string
- 'null'
archived:
type: boolean
favorited:
type: boolean
position:
type:
- integer
- 'null'
description: Sort position among its siblings; null when unpositioned.
lock_version:
type: integer
description: Increases on every save. Send it back as expected_lock_version to guard a write.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
last_edited_by:
type:
- string
- 'null'
description: Email address of whoever saved the note last.
last_edited_by_agent:
type:
- string
- 'null'
description: Name of the agent or token that saved last, if one did.
tag_list:
type: string
description: Tag names joined by a comma and a space, e.g. "planning, review".
tags:
type: array
items:
$ref: '#/components/schemas/TagRef'
container:
description: The folder the note is filed in; null for the inbox.
oneOf:
- $ref: '#/components/schemas/ContainerRef'
- type: 'null'
team:
$ref: '#/components/schemas/TeamRef'
files:
type: array
items:
$ref: '#/components/schemas/NoteAttachment'
Tag:
type: object
required:
- id
- name
- created_at
- updated_at
- notes_count
properties:
id:
type: integer
name:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
notes_count:
type: integer
description: Notes carrying the tag that are not in the Trash, archived included.
NoteAttachment:
type: object
description: A file attached to a note.
required:
- id
- description
- filename
- content_type
- byte_size
- url
- url_expires_at
properties:
id:
type: integer
description:
type:
- string
- 'null'
filename:
type: string
content_type:
type:
- string
- 'null'
byte_size:
type: integer
url:
type: string
format: uri
description: Signed download link that works without a token until `url_expires_at` (15 minutes). Treat it as a credential and do not store it; fetch the note again for a fresh one.
url_expires_at:
type: string
format: date-time
ContainerRef:
type: object
required:
- id
- name
properties:
id:
type: integer
name:
type: string
Error:
type: object
description: 'Every error response carries `error`. The generic statuses also carry `code`, `message` and `docs_url`: 401, a 403 for an agent token outside its grants, and any status answered for a raised error or an unknown endpoint (400, 404, 5xx). Plan-limit 403s carry `upgrade_url` instead. Some endpoint-specific errors carry `error` alone.'
required:
- error
properties:
status:
type: integer
description: The HTTP status again. Present on errors raised inside the app and on unknown endpoints (400, 404, 5xx), not on 401 or the 403s.
error:
type: string
description: Human-readable message.
errors:
type: array
items:
type: string
description: 'Only on a validation failure raised inside the app (422): one message per problem.'
code:
type: string
description: 'Machine-readable code: `unauthorized` (401), `out_of_scope` (403, agent token), or for a raised error or unknown endpoint the snake_case name of the HTTP status, such as `bad_request`, `not_found`, `unprocessable_content` or `internal_server_error`.'
examples:
- unauthorized
- out_of_scope
- not_found
- internal_server_error
message:
type: string
description: What to do about it.
docs_url:
type: string
format: uri
description: The documentation page that explains this error.
upgrade_url:
type: string
format: uri-reference
description: Only on plan-limit 403s. A path on hjarni.com (relative, so prefix https://hjarni.com) to the checkout that lifts the limit.
team_url:
type: string
format: uri-reference
description: Only on the personal note-limit 403 from POST /notes. A relative path to creating a team, the other way past the personal limit.
TagRef:
type: object
required:
- id
- name
properties:
id:
type: integer
name:
type: string
ValidationErrors:
type: object
description: The 422 model-validation shape, also used by a few 409s.
required:
- errors
properties:
errors:
type: array
items:
type: string
description: One human-readable message per problem.
requestBodies:
TagUpdate:
required: true
description: Fields go in a `tag` object; a top-level `name` is wrapped into it for you.
content:
application/json:
schema:
type: object
required:
- tag
properties:
tag:
type: object
properties:
name:
type: string
description: The new name, unique per person.
example:
tag:
name: new-name
TagCreate:
required: true
description: Fields go in a `tag` object; a top-level `name` is wrapped into it for you.
content:
application/json:
schema:
type: object
required:
- tag
properties:
tag:
type: object
required:
- name
properties:
name:
type: string
description: The tag name, unique per person. Stored as sent (not lowercased).
example:
tag:
name: research
headers:
Retry-After:
description: Seconds until the current rate-limit window ends.
schema:
type: integer
X-Per-Page:
description: The page size used, after clamping.
schema:
type: integer
X-Total-Count:
description: Total number of records matching the query, across all pages.
schema:
type: integer
X-Page:
description: The page returned.
schema:
type: integer
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'An API token in `Authorization: Bearer <token>`. Tokens are created in Settings > Connections, or issued to an assistant through the OAuth flow. A new token made in Settings reaches only the folders it is given, at Read or Write per folder, unless it is made with Everything I can reach (see https://hjarni.com/docs/agent-tokens); a token issued through OAuth acts with the person''s full access. A missing, unknown or expired token answers 401.'
externalDocs:
url: https://hjarni.com/docs/api