spintax-net Links API

The Links API from spintax-net — 4 operation(s) for links.

Operations 7

GET /v1/links/{id} One link (scope links) #
PATCH /v1/links/{id} Change the URL or the rules (scope links); the code stays #
DELETE /v1/links/{id} Remove the link (scope links); its code is freed, the link stays as `deleted`… #
GET /v1/links/{id}/qr.svg The QR code of the link (scope links) — an SVG of `https:///?q` #
GET /v1/links/{id}/report Clicks of the link (scope reports); with breakdowns=1 also sources, countries… #

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/spintax-net:spintax-net-links-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

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

spintax-net-links-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: clx management Links API
  version: 0.8.0
  description: 'The /v1 API of clx (docs/spec.md §15). The clx.cx page is a thin client over the same endpoints.

    Authorization: `Bearer ` from a signed-in page, or `Bearer clx_…` — an API key

    (one on the `free` plan, up to 5 on `api`). Every POST, PUT, PATCH and DELETE takes an `Idempotency-Key` header, required

    with an API key; a repeat with the same body replays the first answer for 24 hours.

    Errors: `{ "error": { "code", "message", "details"? } }`. This file grows with each stage; a

    test checks that every route in the code is here and back.'
servers:
- url: https://clx.cx
security:
- bearer: []
tags:
- name: Links
paths:
  /v1/links:
    get:
      summary: Links (scope links); `account_id` narrows to one account
      parameters:
      - name: account_id
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  links:
                    type: array
                    items:
                      $ref: '#/components/schemas/Link'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: getV1Links
      x-operation-id-source: derived
    post:
      summary: Add a short link (scope links) on one of the account's link hosts
      description: 'A click is a `302` to the first matching rule''s URL, else `url`; it is counted as a view of

        the link, with source `qr` when the address carries `?q` (the QR code''s) — or as a bot when

        the client is one (curl, a script, a crawler: it is redirected all the same). `config` turns

        `synced` once the worker has the link (seconds).'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
              - link_host_id
              - url
              properties:
                link_host_id:
                  type: string
                  description: the link host it lives on (POST /v1/link-hosts)
                code:
                  type: string
                  pattern: ^[A-Za-z0-9_-]{3,32}$
                  description: a random 6 characters when not given
                url:
                  type: string
                  format: uri
                  description: https:// only, up to 2048 characters, not on a link host of the account
                rules:
                  type: array
                  maxItems: 10
                  items:
                    $ref: '#/components/schemas/Rule'
      responses:
        '201':
          description: Added
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/Link'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: postV1Links
      x-operation-id-source: derived
  /v1/links/{id}:
    get:
      summary: One link (scope links)
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/Link'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: getV1LinksById
      x-operation-id-source: derived
    patch:
      summary: Change the URL or the rules (scope links); the code stays
      parameters:
      - $ref: '#/components/parameters/Id'
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                rules:
                  type: array
                  maxItems: 10
                  items:
                    $ref: '#/components/schemas/Rule'
      responses:
        '200':
          description: Changed
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/Link'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: patchV1LinksById
      x-operation-id-source: derived
    delete:
      summary: Remove the link (scope links); its code is freed, the link stays as `deleted`…
      parameters:
      - $ref: '#/components/parameters/Id'
      - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/Link'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: deleteV1LinksById
      x-operation-id-source: derived
  /v1/links/{id}/qr.svg:
    get:
      summary: The QR code of the link (scope links) — an SVG of `https:///?q`
      description: 'The `?q` makes a scan count with source `qr`; the worker does not pass it on. One `` and

        one ``, integer coordinates, a quiet zone of 4 modules, error correction M. Without

        `size` the SVG has no width and height and scales to its container. It carries the link''s own

        host, which never changes; deleting that host stops its printed codes.'
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: size
        in: query
        required: false
        schema:
          type: integer
          minimum: 64
          maximum: 9999
        description: pixels
      responses:
        '200':
          description: The SVG
          content:
            image/svg+xml:
              schema:
                type: string
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: getV1LinksByIdQrSvg
      x-operation-id-source: derived
  /v1/links/{id}/report:
    get:
      summary: Clicks of the link (scope reports); with breakdowns=1 also sources, countries…
      description: 'Closed days come from clx.cx; today (and any day the worker has not sent yet) is read live

        from the account''s own database in one read with the breakdowns — `as_of` is the moment of

        that read (cached up to 5 minutes, so it can be that much older than the answer). When that database

        cannot be read, the totals hold clx.cx''s days only, the series of `today` is empty and

        `unavailable` says why. `incomplete` is always false: a link''s days are never refused.'
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: period
        in: query
        schema:
          type: string
          enum:
          - today
          - 7d
          - 30d
          default: today
      - name: breakdowns
        in: query
        schema:
          type: string
          enum:
          - '1'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  report:
                    $ref: '#/components/schemas/Report'
        default:
          $ref: '#/components/responses/Error'
      tags:
      - Links
      operationId: getV1LinksByIdReport
      x-operation-id-source: derived
components:
  schemas:
    Report:
      type: object
      properties:
        period:
          type: string
          enum:
          - today
          - 7d
          - 30d
        from:
          type: string
          format: date
          description: first UTC day
        to:
          type: string
          format: date
          description: last UTC day — today
        as_of:
          type:
          - string
          - 'null'
          format: date-time
          description: 'today is counted up to this moment. A site: the end of the latest closed hour the worker sent (hours arrive up to an hour late), null before its first push. A link: the moment of the live read of the account''s database'
        totals:
          type: object
          properties:
            views:
              type: integer
            bots:
              type: integer
            visitors:
              type: integer
              description: the sum of daily unique visitors
        series:
          type: array
          description: today — one point per hour up to as_of; 7d/30d — one per day (with visitors)
          items:
            type: object
            properties:
              at:
                type: string
                format: date-time
              views:
                type: integer
              bots:
                type: integer
              visitors:
                type: integer
        incomplete:
          type: boolean
          description: today's hours were refused over the account's write budget; daily totals stay exact
        breakdowns:
          type:
          - object
          - 'null'
          description: only with breakdowns=1; read from the account's own database, null when unavailable
          properties:
            pages:
              $ref: '#/components/schemas/Top'
            sources:
              $ref: '#/components/schemas/Top'
            countries:
              $ref: '#/components/schemas/Top'
            devices:
              $ref: '#/components/schemas/Top'
            browsers:
              $ref: '#/components/schemas/Top'
            os:
              $ref: '#/components/schemas/Top'
            bots:
              $ref: '#/components/schemas/Top'
            flows:
              type: array
              maxItems: 200
              description: 'a link only: clicks by source and destination together, the top 200 by count. `to` is where the clicks were sent: the matched rule''s conditions and URL, `AT,DE;mobile>https://…` (sorted; past 40 countries the first 40 and `+N#<fingerprint of the whole list>`), or `>https://…` for the main URL (the first 512 characters, also the keys of a link''s `pages`); "" for clicks counted by a worker that did not record it yet, "(other)" for what went over the detail caps'
              items:
                type: object
                properties:
                  source:
                    type: string
                  to:
                    type: string
                  n:
                    type: integer
        unavailable:
          type: string
          enum:
          - not_installed
          - cloudflare
          - rate_limited
          description: 'why breakdowns (and a link''s today) are not there: the worker is not installed or the token is gone; Cloudflare failed or took over 5 s; over 30 reads of the account databases a minute for this clx user (each cached 5 minutes)'
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: 'Stable codes: unauthorized, invalid_request, not_found, payload_too_large,

                limit_reached, service_full, idempotency_conflict, operation_in_progress,

                scope_required, session_required, key_already_issued,

                account_taken, already_connected, account_not_ready, invalid_bootstrap,

                bootstrap_permission_error, permission_catalog, orphan_not_deleted,

                token_rights_mismatch, token_check_failed, renew_lost, rate_limited, cloudflare_unavailable,

                not_configured, internal. In an account''s or operation''s `error` (the install, §4):

                name_taken, resource_drift, cron_limit, permission_error, revoked, self_check_timeout,

                credentials_missing, credentials_unreadable (clx cannot open the stored token),

                cloudflare_error. Sites: zone_not_found, site_exists, route_conflict, site_not_active,

                update_required (external sites need a newer clx-edge),

                route_not_ours (a route clx made was changed outside clx and left in place).

                Sign-up: email_unconfirmed (POST /v1/accounts before the address is confirmed),

                invalid_password, balance_not_empty (DELETE /v1/me while money is on the balance or the partner wallet or still moving, §18).

                Billing (docs/spec.md §18): insufficient_balance (Pro needs $9 on the balance), invoice_limit (3 pending top-ups),

                payments_unavailable (crypto top-ups not set up or the processor down),

                refund_unavailable (a top-up never credited, older than 90 days or refunded

                already), refund_open (a refund of that top-up is asked for already), paused (an

                object beyond the free plan''s limits: it can be read and deleted, not changed).

                Links: link_exists (the code is taken on that link host), link_host_not_ready (links go

                on an active host only), link_host_in_use

                (DELETE /v1/link-hosts/{id} while live links are on it). In a link host''s `error`:

                dns_record_exists (the named host already has a DNS record — never taken over),

                domain_taken (another worker''s custom domain), domain_not_ours.

                '
            message:
              type: string
            details:
              type: object
    Top:
      type: array
      maxItems: 10
      description: the top 10 by count; "(other)" holds what went over the detail caps
      items:
        type: object
        properties:
          key:
            type: string
          n:
            type: integer
    Rule:
      type: object
      required:
      - url
      description: matches when the visitor's country is in countries (if given) and their device in devices (if given)
      properties:
        countries:
          type: array
          items:
            type: string
            pattern: ^[A-Z]{2}$
        devices:
          type: array
          items:
            type: string
            enum:
            - mobile
            - desktop
        url:
          type: string
          format: uri
          description: https:// only, up to 2048 characters, not on a link host of the account
    Link:
      type: object
      properties:
        id:
          type: string
        account_id:
          type: string
        link_host_id:
          type: string
        code:
          type: string
          description: unique on its link host
        short_url:
          type:
          - string
          - 'null'
          description: https://<link host>/<code>
        url:
          type: string
          description: where it leads when no rule matches
        rules:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/Rule'
          description: the first match wins
        state:
          type: string
          enum:
          - active
          - deleted
        config:
          type: string
          enum:
          - pending
          - synced
          description: whether the worker has this version of the link yet
        paused_at:
          type:
          - string
          - 'null'
          format: date-time
          description: 'set while the link is beyond the free plan''s limits (docs/spec.md §18): it answers 404 and cannot be changed'
        clicks_7d:
          type: integer
          description: clicks of the last 7 closed UTC days (GET /v1/links only, and only with the reports scope)
        created_at:
          type: string
          format: date-time
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        minLength: 1
        maxLength: 200
        pattern: ^[!-~]{1,200}$
    Id:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{1,64}$
        description: 'an id of clx: 22 characters of base64url'
  responses:
    Error:
      description: An error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearer:
      type: http
      scheme: bearer