Documentation Index
Fetch the complete documentation index at: https://docs.dacard.ai/llms.txt
Use this file to discover all available pages before exploring further.
The Dacard.ai API runs three kinds of throttling. They stack. A request must clear every layer to succeed.
- Per-action rate limits, short-window throttles on burst behavior. Backed by
credit_usage. Source: packages/core/src/rate-limit.ts.
- Plan quotas, monthly credit pools and feature gates. Source:
packages/shared/src/plans.ts.
- Per-tier API call ceilings, Business and Enterprise only. Free and Pro do not include programmatic API access.
Per-action rate limits
| Action | Limit | Window | Endpoint |
|---|
score | 5 requests | 60 seconds | POST /api/score, POST /api/score/quick, POST /api/score/product |
chat | 30 requests | 60 seconds | POST /api/chat |
Hits are tracked per userId against a 60-second sliding window. The limit returns 429 Too Many Requests with a Retry-After header that matches the window.
If the rate-limit infrastructure cannot reach the database, the gate fails closed. The request is denied with Retry-After: 60 rather than allowed through. This is intentional: silent rate-limit bypass is a worse failure mode than a brief outage.
There is no global per-action rate limit on api (API key calls). API key calls are tracked but enforced through the per-tier monthly ceiling below.
Plan quotas
| Plan | Monthly credits | Scores (10 credits each) | Chat messages (1 credit each) | API calls/mo | Products | Seats |
|---|
| Free | 80 | 3 | 30 | 0 | 1 | 1 |
| Pro | 1,000 | up to 100 | up to 1,000 | 0 | 5 | 3 |
| Business | 2,000 | up to 200 | up to 2,000 | 25,000 | 25 | 10 |
| Enterprise | Unlimited | Unlimited | Unlimited | 100,000 | Unlimited | Custom |
Free and Pro do not include programmatic API access. Mint API keys on Business or Enterprise from Settings > API Keys. The credit pool resets at the start of each billing cycle. Score packs (one-time top-ups) carry over.
Quota numerics live in packages/shared/src/plans.ts. If the table above drifts from the source file, the source file wins. Open an issue.
Check live consumption with Get Usage and Quota:
curl -X GET https://app.dacard.ai/api/usage \
-H "Authorization: Bearer $DACARD_API_KEY"
Anonymous scoring
POST /api/score (without a session) and POST /api/score/quick accept anonymous reads but cap them tightly.
- 1 anonymous read per IP per hour.
- Result is held until a sign-up links it via
POST /api/score/link.
- No history, no portfolio, no DAC chat.
Authenticated callers should always carry a session or API key. Anonymous fallthrough is a sign-up funnel, not a production flow.
429 response shape
Per-action and per-IP rate limits both return the same shape:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
{
"error": {
"code": "SCORING_RATE_LIMITED",
"message": "Too many reads in a row.",
"action": "Give it 60 seconds and try again.",
"retryable": true
}
}
Plan-quota exhaustion returns 402 Payment Required with code: "CREDIT_EXHAUSTED" or code: "PLAN_LIMIT_REACHED". Those are not retryable. See Errors for the full catalog.
Exponential backoff
The reference pattern below handles per-action 429s. Plan-quota errors should not be retried.
async function callWithBackoff<T>(fn: () => Promise<Response>): Promise<T> {
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fn();
if (res.ok) return res.json() as Promise<T>;
if (res.status !== 429) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error?.message ?? `HTTP ${res.status}`);
}
const retryAfter = Number(res.headers.get('Retry-After') ?? 60);
const jitter = Math.random() * 0.25 * retryAfter;
const delay = (retryAfter + jitter) * 1000 * Math.pow(1.5, attempt);
await new Promise((r) => setTimeout(r, delay));
}
throw new Error('Exceeded max retries on 429');
}
const score = await callWithBackoff<ScoreResult>(() =>
fetch('https://app.dacard.ai/api/score', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DACARD_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: 'https://example.com' }),
}),
);
Cap retries. A user staring at a spinner is a worse experience than a clear error.
Score packs
Add scoring capacity to any paid plan without upgrading the tier.
| Pack | Scores | Price |
|---|
| Small | 20 | $29 |
| Medium | 50 | $49 |
| Large | 150 | $99 |
Score packs do not expire. Plan credits reset each cycle regardless of pack balance.
Upgrading
Upgrade from Settings > Billing in the dashboard, or programmatically via Create Checkout Session. Enterprise customers can negotiate custom limits and SLAs.