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.
https://mask.pk/api/v1All 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.
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.
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed per window (60) |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Seconds until the current window resets |
| Retry-After | Seconds 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.
{
"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": "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": "`from` must be on or before `to`.",
"field": "from"
}| Status | Description |
|---|---|
| 200 | Request succeeded |
| 201 | Resource created |
| 207 | Deleting a link succeeded but a cleanup step did not. See result.steps. |
| 400 | Bad request — malformed JSON or missing required fields |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — API key lacks required scope |
| 404 | Resource not found |
| 409 | Conflict — resource already exists (e.g. duplicate slug) |
| 413 | Request body too large |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
| 503 | The 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.
| Parameter | Type | Description |
|---|---|---|
| limit | integer | Number of items per page (default: 20, max: 100) |
| page | integer | 1-based page number (default: 1) |
curl "https://mask.pk/api/v1/links?limit=10&page=2" \
-H "Authorization: Bearer mk_3f9a1c7e5b208d46a1f0c93e78b25d4610fa8c3729de154bb06e7a9f2c8d3510"{
"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.