Skip to main content

Base URL

https://app.puffle.ai
Endpoint paths in this reference include their /api prefix, for example GET /api/account. Combine the base URL and documented path directly:
curl -H "Authorization: Bearer pk_live_abc123" \
  "https://app.puffle.ai/api/account"
HTTPS is required on all requests.

Authentication

All public API requests require a Puffle API key as a Bearer token in the Authorization header. API keys are prefixed with pk_live_.
Authorization: Bearer pk_live_abc123...

Generating an API key

  1. Log in to the Puffle dashboard
  2. Navigate to Settings -> API
  3. Click Generate API key
  4. Copy and store the key. It is shown only once.
Each account has one active API key at a time. To rotate credentials, generate and deploy the new key first, then revoke the old one.

Revoking an API key

Keys can be revoked from the dashboard or through the session-authenticated /api/user/api-key API key endpoint. Revocation is immediate, and in-flight requests using the revoked key fail with 401.

Session authentication

A small set of API key endpoints, specifically those that create, view, and revoke API keys (/api/user/api-key), use session-based authentication through browser cookies rather than Bearer tokens. These are designed to be called from the Puffle dashboard UI, not from your backend integration.

Security best practices

API keys grant full access to your Puffle account. Always make API calls from your backend, never from browser JavaScript or mobile apps where the key could be extracted.
Store your key in an environment variable (PUFFLE_API_KEY) and reference it in code. Never hard-code keys or commit them to version control.
Generate a new key and update your services before revoking the old one to avoid downtime.
Use the Puffle dashboard to review API key usage. If you detect unexpected activity, revoke the key immediately.

Rate Limits

Rate limits are endpoint-specific. Some routes enforce their own cooldowns or protective limits, and upstream providers may also return rate-limit errors. The endpoint page is the source of truth for request size and polling guidance. If you receive a 429 Too Many Requests response, respect the Retry-After header when it is present. If there is no Retry-After, back off exponentially and avoid tight polling loops.

Rate-limit response

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please retry after 60 seconds."
  }
}

Handling rate limits

1

Detect the 429 status

Check for HTTP status 429 in every response before processing the body.
2

Read the Retry-After header

Wait the number of seconds specified in Retry-After before retrying.
3

Use exponential backoff

If Retry-After is not present, implement exponential backoff starting at 1 second.
4

Avoid polling loops

For high-frequency use cases, avoid tight polling loops and back off aggressively after 429 responses.
When paginating through large result sets, space requests out and follow the limit documented for that endpoint.

Errors

Public Bearer-authenticated routes generally return the structured error envelope below for framework-level authentication, method, validation, and typed route errors:
{
  "error": {
    "code": "error_code_string",
    "message": "Human-readable description of the error."
  }
}
Some route-specific errors still return the older flat shape:
{
  "error": "Human-readable description of the error."
}
Check the endpoint page examples for the exact response shape an operation can return. Treat error.code as stable when it is present; otherwise handle the HTTP status and human-readable error message.

HTTP Status Codes

StatusMeaningWhen It Happens
200OKSuccessful GET, PATCH, PUT, or POST that returns data
201CreatedSuccessful resource creation (POST)
204No ContentSuccessful deletion or action with no response body
400Bad RequestInvalid body, missing required fields, or constraint violation
401UnauthorizedMissing or invalid Bearer token / session
403ForbiddenValid auth but insufficient permissions
404Not FoundResource does not exist or does not belong to your account
409ConflictResource already exists (e.g., duplicate signal type name)
422Unprocessable EntityWell-formed but semantically invalid request
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error

Error Codes

CodeDescription
unauthorizedMissing or invalid authentication credentials
not_foundThe requested resource does not exist
invalid_requestBody or parameters are invalid, or a constraint was violated
rate_limitedEndpoint or provider rate limit hit
conflictResource already exists
internal_errorUnexpected server error

Pagination

Pagination is endpoint-specific. Most Leads, Signals, and Enrichment list endpoints use cursor pagination; Unibox and some Campaign endpoints use page; Lead Search task results use offset. Use the query parameters shown on each endpoint page.

Common cursor parameters

ParameterTypeDescription
limitintegerItems to return. Maximum and default vary by endpoint.
cursorstringID of the last item from the previous page.

Response Shape

{
  "signals": ["..."],
  "pagination": {
    "cursor": "660e8400-e29b-41d4-a716-446655440099",
    "hasMore": true,
    "count": 100
  }
}
When cursor is null and hasMore is false, you have reached the last page. count is the number of items on the current page. For endpoints that return page, offset, total, or nextCursor instead, follow the response fields documented on that endpoint.

Iterating All Pages

# First page
curl -s -H "Authorization: Bearer pk_live_abc123" \
  "https://app.puffle.ai/api/signals?limit=100"

# Subsequent pages — pass cursor from previous response's pagination.cursor
curl -s -H "Authorization: Bearer pk_live_abc123" \
  "https://app.puffle.ai/api/signals?limit=100&cursor=660e8400-e29b-41d4-a716-446655440099"