Hjarni Search API

Full-text search over notes.

GitHub OpenAPI

Operations 1

GET /search Search notes #

Work with this as data

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-search-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 Specification

hjarni-com-search-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hjarni REST Search 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: Search
  description: Full-text search over notes.
paths:
  /search:
    get:
      operationId: searchNotes
      summary: Search notes
      description: Full-text search over active notes (archived and trashed notes are left out), ranked by relevance when `q` is given. With a full-access token `search_scope` chooses personal notes, one team's notes, or both (`all`, the default); an agent token searches what its grants reach and is refused with 403 when it names a team it reaches nothing in. Paginated.
      tags:
      - Search
      parameters:
      - name: q
        in: query
        description: Search terms. Omitted, blank, or punctuation only, the scope is returned unfiltered and in no particular order.
        schema:
          type: string
      - name: search_scope
        in: query
        description: '`personal` for personal notes only, `team:<id>` for one team''s notes, or `all` for personal notes plus every team the person belongs to. Any other value is treated as `all`. A `team:<id>` the person is not a member of answers 404.'
        schema:
          type: string
          pattern: ^(personal|all|team:[0-9]+)$
          default: all
        example: personal
      - $ref: '#/components/parameters/ExcludeBody'
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      responses:
        '200':
          description: One page of matching notes.
          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'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  schemas:
    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
    TeamRef:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: integer
        name:
          type:
          - string
          - 'null'
    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.
    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'
    TagRef:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: integer
        name:
          type: string
  responses:
    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.
    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).
    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
    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
    ExcludeBody:
      name: exclude_body
      in: query
      description: '`true` leaves `body` out of every note, so a sync client can page through metadata cheaply and fetch bodies one at a time with GET /notes/{id}.'
      schema:
        type: boolean
        default: false
  headers:
    X-Per-Page:
      description: The page size used, after clamping.
      schema:
        type: integer
    Retry-After:
      description: Seconds until the current rate-limit window ends.
      schema:
        type: integer
    X-Page:
      description: The page returned.
      schema:
        type: integer
    X-Total-Count:
      description: Total number of records matching the query, across all pages.
      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