QR code API and MCP server

Create dynamic QR codes and read their scan statistics from your own code or from an AI agent. A REST API and a remote MCP server, both on the same API keys.

Quickstart

  1. Sign in to the dashboard and create an API key on the Keys page. The key starts with qrt_ and is shown once, so store it now.
  2. Create a dynamic link. The response has the short link, and qrText, the text to encode in the QR code (encode it exactly as given, in upper case).
  3. Read its scan statistics. Counts are per complete UTC day and are always estimates.
Create a link
curl -X POST https://track.qrcode-alligator.com/api/v1/links \
  -H "Authorization: Bearer qrt_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/spring-menu"}'
Read its scan statistics
curl https://track.qrcode-alligator.com/api/v1/links/CODE/stats \
  -H "Authorization: Bearer qrt_YOUR_KEY"

Replace CODE with the code from the create response. Keys can also list, change, pause and delete links; see the reference below.

MCP server

The MCP server is at https://track.qrcode-alligator.com/api/mcp: Streamable HTTP, stateless, plain JSON answers. It takes the same API key as the REST API, as Authorization: Bearer qrt_…, and every request needs one. A tool call is a REST call made with your key, so validation, limits and the audit trail are the API's own.

Tools: create_link, list_links, get_link, update_link and get_link_stats. There is no delete tool: an agent can pause a link, but removing one stays with a person. A refused call comes back as a tool result with isError set, for the agent to read.

Replace qrt_YOUR_KEY with your key. Keep the key out of files you commit.

Claude Code

Run once in a terminal.

Claude Code
claude mcp add --transport http qr-alligator https://track.qrcode-alligator.com/api/mcp \
  --header "Authorization: Bearer qrt_YOUR_KEY"

Claude Desktop

Add to claude_desktop_config.json. Claude Desktop starts local servers, so mcp-remote (needs Node.js) bridges to the remote one.

Claude Desktop
{
  "mcpServers": {
    "qr-alligator": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://track.qrcode-alligator.com/api/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": { "AUTH_HEADER": "Bearer qrt_YOUR_KEY" }
    }
  }
}

Cursor

Add to .cursor/mcp.json (or ~/.cursor/mcp.json).

Cursor
{
  "mcpServers": {
    "qr-alligator": {
      "url": "https://track.qrcode-alligator.com/api/mcp",
      "headers": { "Authorization": "Bearer qrt_YOUR_KEY" }
    }
  }
}

Any other client

Point it at the endpoint with the header. To check the connection by hand, list the tools:

Any other client
curl -X POST https://track.qrcode-alligator.com/api/mcp \
  -H "Authorization: Bearer qrt_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

API reference

Base URL https://track.qrcode-alligator.com/api/v1. Authenticate with Authorization: Bearer qrt_…. This reference is generated from the OpenAPI document when the site is built. It lists the operations an API key can call; key management, claiming and account deletion are for the signed-in dashboard only.

Links

POST/api/v1/links

Create a short link: a redirect (kind url) or a contact card (kind contact). Anonymous links need Turnstile, expire in 7 days unless claimed, and return a one-time claimToken.

Access: API key (anonymous allowed)

Request body (JSON)
FieldTypeNotes
kind"url" | "contact"url (default): the code redirects to url. contact: a scan is answered with a vCard built from payload — no page in between, so the person scanning needs a connection and gets the card through their browser.
urlstring (uri)Required for kind url. Up to 2048 characters.
payloadobjectRequired for kind contact.
payload.firstNamestringUp to 60 characters.
payload.lastNamestringUp to 60 characters.
payload.organizationstringUp to 100 characters.
payload.titlestringUp to 100 characters.
payload.streetstringUp to 120 characters.
payload.citystringUp to 60 characters.
payload.statestringUp to 60 characters.
payload.zipcodestringUp to 20 characters.
payload.countrystringUp to 60 characters.
payload.notestringUp to 280 characters.
payload.phoneMobilestringUp to 32 characters.
payload.phoneWorkstringUp to 32 characters.
payload.phoneHomestringUp to 32 characters.
payload.emailstring (email)Up to 254 characters.
payload.websitestring (uri)Up to 2048 characters.
turnstileTokenstringRequired only when creating anonymously (no key, no session).
Responses
  • 201 The link: code, kind, shortUrl, qrText (encode this, uppercase), expiresAt, claimToken (anonymous only).
  • 401 The API key is invalid or revoked.
  • 403 Turnstile failed (anonymous only), or { error: "plan_limit", limit: "active_codes", max, used, plan, message }: the account is at its plan's active tracked codes. Pause or delete a code to make room. Or: The key's owner hasn't agreed to the current terms (terms_required). They agree by signing in to the dashboard.
  • 422 { error: "destination_refused", reasons }: the destination, or a contact's website, is unsafe, self-referencing or blocklisted. { error: "content_refused", field, reason }: a contact field broke a content rule.
  • 429 Creation quota exceeded. Or: API key rate limit: 60/minute or 10,000/day per key. See Retry-After. Or { error: "plan_limit", limit: "api_daily", max, used, plan, resetsAt, message }: the account's daily API calls on its plan (all keys together) are used up until 00:00 UTC.
  • 503 { error: "contact_links_disabled" }: switched off by the operator.

Scan statistics

Account

GET/api/v1/me

The account behind this key or session.

Access: API key

Responses
  • 200 { email, termsAccepted, notices }: notices lists the one-time dashboard notices not yet dismissed. Sessions also get the plan block and trial ({ offered, active }, docs/trial.md).
  • 401 Not signed in / invalid key. Or: Not signed in, or the API key is invalid or revoked.
  • 403 The key's owner hasn't agreed to the current terms (terms_required). They agree by signing in to the dashboard.
  • 429 API key rate limit: 60/minute or 10,000/day per key. See Retry-After. Or { error: "plan_limit", limit: "api_daily", max, used, plan, resetsAt, message }: the account's daily API calls on its plan (all keys together) are used up until 00:00 UTC.

GET/api/v1/limits

Where the account stands against its plan: active tracked codes and today's API calls (per account, all keys together; resets 00:00 UTC). max null = no cap on this plan.

Access: API key

Responses
  • 200 { plan, activeCodes: { used, max }, apiCalls: { used, max, resetsAt }, headsUp: [limit] } — headsUp lists the limits at 80% or more.
  • 401 Not signed in, or the API key is invalid or revoked.
  • 403 The key's owner hasn't agreed to the current terms (terms_required). They agree by signing in to the dashboard.
  • 429 API key rate limit: 60/minute or 10,000/day per key. See Retry-After. Or { error: "plan_limit", limit: "api_daily", max, used, plan, resetsAt, message }: the account's daily API calls on its plan (all keys together) are used up until 00:00 UTC.

API description

GET/api/v1/openapi.json

This API description (OpenAPI 3.1).

Access: No authentication

Responses
  • 200 The OpenAPI document.

Limits and errors

Every API key is limited to about 60 requests a minute (best effort), and the request that goes over gets 429 with a Retry-After header. Accounts created on or after November 1, 2026 also have a daily limit of 100 API calls and 100 active codes, counted per account across all its keys and the MCP server. Older accounts keep 10,000 calls a day per key and no cap on active codes. A day is a calendar day in UTC.

Every response to an API-key request carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (a Unix time) for the daily quota. Past the daily API limit you get 429 with error plan_limit and a Retry-After until 00:00 UTC; creating or reactivating a code past the active-code limit gets 403 plan_limit. The body says which limit, its maximum and how much is used. Pausing or deleting a code frees a slot, and reaching a limit never stops a scan.

Errors are JSON with an error code and a message. 401 means the key is missing, invalid or revoked (with a WWW-Authenticate header on the MCP server); 403 terms_required means the key's owner has not agreed to the current terms yet, which they do by signing in to the dashboard; 404 is also what you get for someone else's link; 422 means a destination was refused.

Plans and prices are on the pricing page.

More

  • openapi.json: the OpenAPI 3.1 document this reference is built from
  • llms.txt: a short map of the site for AI assistants
  • Terms of Service, including API and MCP use
  • Security issues: security@qrcode-alligator.com (see Security)