API reference
REST API for Pro and Lifetime members — authentication, profiles, analytics, and rate limits.
https://belcard.app/api/v1. Authentication
Generate an API token from Dashboard → Settings → API tokens → New token. Pass the token as a Bearer header on every request:
Authorization: Bearer bc_live_xxxxxxxxxxxxxxxxxxxxxxxx Token scopes
Choose which permissions to grant when creating a token. A token can have multiple scopes.
| Scope | What it allows |
|---|---|
profile:read | Read your profile, links, and settings. |
profile:write | Update display name, tagline, bio, links, and card theme. |
analytics:read | Read analytics events, summaries, and export data. |
Tokens expire after 1 year by default. Set Never expires at creation time for persistent integrations. Revoke any token instantly from Dashboard → Settings → API tokens.
Profile endpoints
GET /api/v1/profile
Returns the authenticated user's profile. Requires profile:read.
curl https://belcard.app/api/v1/profile \
-H "Authorization: Bearer bc_live_xxx" {
"id": "a1b2c3d4e5f6...",
"username": "aayush",
"displayName": "Aayush Sharma",
"tagline": "Product Designer · Kathmandu",
"bio": "Building things people use.",
"cardTheme": "void",
"isPublic": true,
"links": [
{ "type": "linkedin", "label": "LinkedIn", "value": "https://linkedin.com/in/aayush" },
{ "type": "email", "label": "Email", "value": "aayush@example.com" },
{ "type": "phone", "label": "Phone", "value": "+977 9812345678" }
],
"createdAt": 1714512000
} PATCH /api/v1/profile
Update your profile. All fields optional — only send what you want to change. Requires profile:write.
curl -X PATCH https://belcard.app/api/v1/profile \
-H "Authorization: Bearer bc_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"tagline": "Senior Engineer · Leapfrog",
"cardTheme": "aurora",
"links": [
{ "type": "github", "label": "GitHub", "value": "https://github.com/aayush" },
{ "type": "url", "label": "Portfolio", "value": "https://aayush.dev" }
]
}' // 200 OK
{ "ok": true } Valid values for cardTheme: "void", "obsidian", "aurora", "solar".
Valid values for link type: "linkedin", "twitter", "github", "email", "phone", "url".
Sending links replaces your entire link list. Include all links you want to keep.
Analytics endpoints
GET /api/v1/analytics/summary
Aggregate event counts for a date range. Defaults to the last 30 days. Requires analytics:read.
curl "https://belcard.app/api/v1/analytics/summary?from=2025-01-01&to=2025-01-31" \
-H "Authorization: Bearer bc_live_xxx" {
"from": "2025-01-01",
"to": "2025-01-31",
"totals": {
"nfcTaps": 142,
"profileViews": 318,
"linkClicks": 87,
"contactSaves": 24
}
} GET /api/v1/analytics/events
Paginated raw event list. Requires analytics:read.
| Query param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Results per page (max 200). |
offset | integer | 0 | Pagination offset. |
type | string | all | Filter by event type (e.g. nfc_tap). |
from | YYYY-MM-DD | 30 days ago | Start date (inclusive). |
to | YYYY-MM-DD | today | End date (inclusive). |
curl "https://belcard.app/api/v1/analytics/events?limit=20&type=nfc_tap" \
-H "Authorization: Bearer bc_live_xxx" {
"events": [
{
"id": 1001,
"eventType": "nfc_tap",
"deviceType": "mobile",
"countryCode": "NP",
"city": "Kathmandu",
"createdAt": 1714512000
}
],
"total": 142,
"limit": 20,
"offset": 0
} Rate limiting
| Plan | Requests / min | Requests / day |
|---|---|---|
| Pro | 60 | 10,000 |
| Lifetime | 120 | 50,000 |
Rate limit state is returned in response headers on every request:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1714512060 When you exceed the limit, the API returns 429 Too Many Requests. Wait until X-RateLimit-Reset (a Unix timestamp) before retrying.
Error responses
All errors follow a consistent envelope:
{
"error": "Unauthorized",
"code": "AUTH_REQUIRED",
"status": 401
} | HTTP status | Code | Cause |
|---|---|---|
| 400 | BAD_REQUEST | Malformed body or missing required field. |
| 401 | AUTH_REQUIRED | Missing or invalid Bearer token. |
| 403 | INSUFFICIENT_SCOPE | Token lacks the required scope. |
| 404 | NOT_FOUND | Resource doesn't exist. |
| 422 | VALIDATION_ERROR | Request body failed schema validation. |
| 429 | RATE_LIMITED | Too many requests. Respect X-RateLimit-Reset. |
| 500 | INTERNAL_ERROR | Server error. Retry after a brief delay or contact support. |
Webhooks Coming soon
Webhooks will POST signed payloads to your endpoint when events occur on your profile — such as an NFC tap, a contact save, or a link click. You'll be able to subscribe per event type. The payload will include a HMAC-SHA256 signature header for verification.
Planned for Q3 2025. Email us if webhooks are important to your use case — it helps us prioritise.