API Reference
The MASK API is a RESTful JSON API for managing short links, bio pages, QR codes, analytics, and webhooks. Every feature available in the dashboard is accessible programmatically.
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_live_abc123def456"Production keys are prefixed with mk_live_ and test keys with mk_test_. See the Authentication guide for details on scopes and key rotation.
Rate Limits
The API enforces a rate limit of 1,000 requests per minute per API key. Rate limit status is communicated via response headers on every request.
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed per window (1000) |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp when the window resets |
| Retry-After | Seconds to wait before retrying (only on 429 responses) |
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": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 12 seconds.",
"retry_after": 12
}
}Error Format
The API uses conventional HTTP status codes to indicate success or failure. All error responses include a JSON body with a consistent structure containing a machine-readable code, a human-readable message, and an optional details array for validation errors.
{
"error": {
"code": "validation_error",
"message": "Request body failed validation.",
"details": [
{
"field": "url",
"message": "Must be a valid URL starting with https://"
},
{
"field": "slug",
"message": "Slug must be between 4 and 64 characters"
}
]
}
}| Status | Description |
|---|---|
| 200 | Request succeeded |
| 201 | Resource created |
| 204 | Resource deleted (no response body) |
| 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) |
| 422 | Validation error — check error.details |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
Pagination
List endpoints return paginated results using cursor-based pagination. Each response includes a pagination object with the cursor for the next page. Pass the cursor as a query parameter to fetch subsequent pages.
| Parameter | Type | Description |
|---|---|---|
| limit | integer | Number of items per page (default: 25, max: 100) |
| cursor | string | Cursor from a previous response to fetch the next page |
curl "https://mask.pk/api/v1/links?limit=10&cursor=eyJpZCI6Imxua18zNDU2In0" \
-H "Authorization: Bearer mk_live_abc123def456"{
"data": [
{ "id": "lnk_abc123", "url": "https://example.com", "slug": "demo" },
{ "id": "lnk_def456", "url": "https://other.com", "slug": "other" }
],
"pagination": {
"has_more": true,
"cursor": "eyJpZCI6Imxua19kZWY0NTYifQ"
}
}When has_more is false, you have reached the last page. The cursor field will be null on the last page.
Request IDs
Every API response includes an X-Request-Id header containing a unique identifier for the request. Include this ID when contacting support to help us debug issues quickly.
HTTP/2 200
Content-Type: application/json
X-Request-Id: req_7f3a2b1c4d5e
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1711296000