Documentation menu

MASK Platform › API Reference

API Reference

The MASK API is a RESTful JSON API for managing short links and QR codes and for recording and reading conversions. It covers part of the dashboard, not all of it: bio pages, campaigns, team management, domains and webhook subscriptions are managed in the dashboard only.

Base URL

All API requests are made to the following base URL over HTTPS. Plain HTTP requests are rejected. All request and response bodies use JSON encoding with Content-Type: application/json.

Base URL
https://mask.pk/api/v1

All endpoint paths in this documentation are relative to the base URL. For example, /links refers to https://mask.pk/api/v1/links.

Authentication

The MASK API uses API keys for authentication. Include your key in the Authorization header as a Bearer token. Keys are scoped to a workspace and can be created from Settings → API Keys.

Authenticated request
curl https://mask.pk/api/v1/links \
  -H "Authorization: Bearer mk_3f9a1c7e5b208d46a1f0c93e78b25d4610fa8c3729de154bb06e7a9f2c8d3510"

Every key is prefixed with mk_ followed by 64 hexadecimal characters. There is one environment and no test mode. See the Authentication guide for details on scopes and key rotation.

The API is a paid feature. Keys on a workspace that is not on Growth or above are refused with 401 Unauthorized, including a key minted while the workspace was still on a paid plan.

Rate Limits

The API enforces a rate limit of 60 requests per minute per API key, shared across every endpoint. Rate limit headers are returned on the 429 response only, not on successful requests.

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per window (60)
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetSeconds until the current window resets
Retry-AfterSeconds to wait before retrying

When the rate limit is exceeded, the API returns a 429 Too Many Requests response. Use the Retry-After header to determine when to retry.

429 response
{
  "error": "Rate limit exceeded"
}

Error Format

The API uses conventional HTTP status codes to indicate success or failure. An error response is a JSON object whose error key holds a human-readable string. There is no error code enum and no details array: one request produces one message, describing the first problem found.

Error response
{
  "error": "Invalid URL"
}

Some responses add one more key. The conversions and goal-report endpoints name the offending query parameter in a field key on validation errors. Responses reporting a feature as unavailable on this deployment carry a stable code such as goals_unavailable. Treat both as optional.

Error response with a field
{
  "error": "`from` must be on or before `to`.",
  "field": "from"
}
StatusDescription
200Request succeeded
201Resource created
207Deleting a link succeeded but a cleanup step did not. See result.steps.
400Bad request — malformed JSON or missing required fields
401Unauthorized — missing or invalid API key
403Forbidden — API key lacks required scope
404Resource not found
409Conflict — resource already exists (e.g. duplicate slug)
413Request body too large
429Rate limit exceeded
500Internal server error
503The feature is not available on this deployment. Retrying will not help.

Pagination

GET /links is the only paginated endpoint, and it pages by number rather than by cursor. The response carries a meta object with the page you asked for and the total row count; divide the total by the limit to know how many pages there are.

ParameterTypeDescription
limitintegerNumber of items per page (default: 20, max: 100)
pageinteger1-based page number (default: 1)
Paginated request
curl "https://mask.pk/api/v1/links?limit=10&page=2" \
  -H "Authorization: Bearer mk_3f9a1c7e5b208d46a1f0c93e78b25d4610fa8c3729de154bb06e7a9f2c8d3510"
Paginated response
{
  "data": [
    { "id": "clx1a2b3c4d5e6f7g8h9i0j1", "slug": "demo", "originalUrl": "https://example.com" },
    { "id": "clx9z8y7x6w5v4u3t2s1r0q9", "slug": "other", "originalUrl": "https://other.com" }
  ],
  "meta": {
    "page": 2,
    "limit": 10,
    "total": 34
  }
}

Links are ordered newest first, and deleted links are excluded from both data and meta.total. Because the order is by creation time, a link created while you are paging can shift rows onto the next page.