Getting Started
Welcome to the Buddy Punch Public API. This guide takes you from zero to your first authenticated request in about ten minutes, then shows you how to paginate, handle rate limits, and parse errors.
Pin a version header (X-Api-Version: 2025-03-01) so your integration keeps working when newer versions ship, and watch the Changelog for breaking changes.
If you're migrating an existing integration from the legacy api.buddypunch.com endpoints, read the Migration Guide after this.
1. Create an API Key
- Log in to Buddy Punch
- Navigate to Settings → Integrations → Developer API → Manage API Keys
- Click Create API Key
- Select the permission scopes your integration needs (for example
employees:read,locations:write) - Copy the key. Production keys start with
bp_live_.
Store your key securely. It cannot be retrieved after creation. If it leaks, revoke it and create a new one.
2. Make Your First Request
Every request sends the key as a Bearer token:
curl https://api2.buddypunch.com/employee \
-H "Authorization: Bearer bp_live_xxxxxxxxxxxxxxxx"
On success you get a 200 OK with a paginated list envelope:
{
"object": "list",
"data": [
{
"id": 4521,
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@example.com",
"isActive": true
}
],
"hasMore": false,
"totalCount": 1,
"url": "/employee"
}
If your key is missing or invalid you get 401 Unauthorized; if the key lacks the required scope you get 403 Forbidden. See the Errors guide for the full catalog.
3. Understanding the Response Envelope
Every list endpoint wraps results in a consistent envelope:
| Field | Type | Description |
|---|---|---|
object |
string | Always "list" for list responses |
data |
array | The items for this page |
hasMore |
boolean | true if more items are available |
totalCount |
integer | Number of items returned, or total known items, depending on the endpoint |
url |
string | The endpoint path that produced this response |
Single-resource responses (like GET /employee/{id}) return the resource object directly without a wrapper.
4. Pagination
List endpoints use cursor-based pagination. Pass limit to control page size (1–100, default 25) and startingAfter to request the next page using the last item's id:
# First page
curl "https://api2.buddypunch.com/employee?limit=50" \
-H "Authorization: Bearer bp_live_xxxxxxxxxxxxxxxx"
# Next page — pass the id of the last item from the previous page
curl "https://api2.buddypunch.com/employee?limit=50&startingAfter=4572" \
-H "Authorization: Bearer bp_live_xxxxxxxxxxxxxxxx"
Loop until hasMore is false. Cursor-based pagination means page boundaries stay stable even while new items are being created.
5. Rate Limits
Requests are limited per account using a sliding-window counter. The per-minute ceiling depends on your Buddy Punch plan:
| Plan tier | Per-account limit |
|---|---|
| Trial / non-paying | 60 requests/minute |
| Pro / Premium / Starter (any paid non-Enterprise) | 600 requests/minute |
| Enterprise | 1,000 requests/minute |
A separate per-API-key DoS guard of 1,000 requests/minute runs alongside the per-account limit to protect the platform — well-behaved integrations rarely hit it.
On a 429 Too Many Requests response the API tells you which limit you hit and when to retry. Two flavors of 429 exist with slightly different headers:
Per-minute rate-limit 429s (error.code = rate_limit_exceeded or account_rate_limit_exceeded):
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per window for the limit you tripped |
X-RateLimit-Remaining |
Always 0 on a 429 — you're exactly at the limit |
Retry-After |
Number of seconds to wait before retrying |
Trial-only 429s (error.code = trial_resource_limit_exceeded, trial_resource_daily_limit_exceeded, or trial_daily_writes_exceeded):
| Header | Description |
|---|---|
Retry-After |
Conservative seconds to wait (resource-ceiling rejections aren't really retryable — the cap won't drop until you delete a resource or upgrade) |
X-RateLimit-* headers are not set on trial-only 429s because the trial caps are volume/count limits, not per-minute rates. Branch on error.code to tell them apart.
On any 429, sleep for Retry-After seconds and try again. Back off exponentially on repeated failures. Successful responses do not currently include these headers — check for 429 status explicitly rather than looking at X-RateLimit-Remaining on every request.
Trial accounts also have a few additional safeguards (smaller per-resource caps, daily write caps, and a one-active-API-key limit enforced in the dashboard) — see the Errors guide for the codes you may encounter.
6. Error Handling
All errors return the same envelope shape:
{
"error": {
"type": "invalid_request_error",
"code": "resource_not_found",
"message": "Employee 999 not found.",
"requestId": "req_abc123"
}
}
type— high-level category (invalid_request_error,authentication_error,authorization_error,rate_limit_error,api_error)code— specific reason within the categorymessage— human-readable explanationrequestId— include this when reporting issues to support
The Errors guide lists every (type, code) pair the API emits along with retry recommendations.
7. Common Pitfalls
- All field names are camelCase.
firstName, notfirst_name.punchInDatetime, notpunch_in_datetime. - Dates are ISO 8601. Use
2026-03-17T08:00:00for local date-times,2026-03-17T15:00:00Zfor UTC. Don't send epoch milliseconds. - API keys start with
bp_live_. Legacy APIM subscription keys and bearer tokens no longer work — see the Migration Guide if you were using those. - Pagination is cursor-based, not offset-based. There is no
page=2oroffset=50. FollowstartingAfterwith the last item'sid. - POST/PUT requests need
Content-Type: application/json. Without it, requests typically fail with415 Unsupported Media Typeor a model-binding error — the body is not silently processed. - Permission scopes are resource-specific. An
employees:readkey cannot list locations — scopes are checked per resource.
8. Next Steps
| Guide | When to read |
|---|---|
| Data Model | You're building a UI and need the field shape of every resource |
| Errors | You want the full error catalog and retry strategy |
| Idempotency | You're making write requests and need safe retries |
| Code Examples | You want copy-paste samples in curl / Node.js / Python / C# |
| Webhooks | You want real-time event notifications instead of polling |
| Migration Guide | You're moving from the legacy API |
The interactive API reference has "Try It" consoles for every endpoint once you paste your key.