Beetle API
Classify a merchant against the right MCC and keep dated evidence of what the merchant sold and which policies it published.
Base URL: https://alpha.api.getbeetle.in · API version 1.0.0
Quickstart
- Create an account in the dashboard.
- Verify your email, then create an API key under Settings → API tokens.
- Send a URL to the classification endpoint.
curl -X POST https://alpha.api.getbeetle.in/ \
-H "Authorization: Bearer $BEETLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://merchant.example"}'
The raw key is shown once at creation. If you lose it, revoke it and create a new one.
Authentication
Every request uses a bearer credential in the Authorization header:
Authorization: Bearer <dashboard_token | api_key>
- API key (
bet_live_…) — classification requests. Keys are scoped to the account and can be revoked at any time. - Dashboard token — returned by
/auth/signupand/auth/login; used for keys, usage, billing, runs, and webhooks.
Missing or invalid credentials return 401. Unknown or foreign run/webhook ids return 404 rather than leaking existence.
API keys
List keys for the account, newest first.
Create a key. Requires a verified email. Body: { "name": "Production" }. Returns the record plus raw, which is never shown again.
Revoke a key immediately. Past usage is retained for audit.
Classify a merchant
Runs the classifier once for a single URL. Every run fetches policy pages; policy-page bodies are never returned.
| Field | Type | Notes |
|---|---|---|
url | string | Required. One URL only; comma-separated values are rejected. |
A run is billable only when at least one policy page resolves to a real URL. MCC alone does not make a run billable. Policy-less runs are recorded as edge_failure, are not billable, and do not consume quota. The response exposes policy-page URLs and confidence scores, not their content.
{
"schema_version": "1.0",
"user_id": 42,
"billable": true,
"payload": {
"request_id": "24a1dc43-ae9d-4650-8eae-95f61d980419",
"created_at": 1762592000000,
"websites": [{
"url": "merchant.example",
"final_url": "https://merchant.example/",
"evidences": { "social": {}, "links": [], "product_pages": [] },
"assessments": {
"policy_pages": {
"terms": { "url": "https://merchant.example/terms", "confidence": 0.91 },
"privacy": { "url": "https://merchant.example/privacy", "confidence": 0.88 },
"legal": { "url": null, "confidence": 0 },
"shipping":{ "url": null, "confidence": 0 },
"refunds": { "url": null, "confidence": 0 }
},
"mcc": [
{ "category": "Computer Programming and IT Services", "code": 7372, "confidence": 0.30 },
{ "category": "Information Retrieval Services", "code": 7375, "confidence": 0.59 }
]
}
}]
}
}
Entry plans have a lifetime allowance of 25 checks. When it is exhausted the API returns 402 until the account upgrades.
Usage
Newest-first usage events. Each event carries the key name and, when a stored run exists, its request_id.
| Query | Description |
|---|---|
from, to | ISO 8601 bounds. |
domain | Case-insensitive substring. |
status | success · edge_failure · error |
key_id | Integer API key id. |
limit, offset | Default 100, max 500; offset for pagination. |
Event fields: id, api_key_id, key_name, request_id (nullable), domain, status, latency_ms, billable, created_at.
Aggregates a range with interval=day|month. Returns totals and dense, zero-filled buckets ordered ascending.
Streams a CSV of the full filtered range (no row cap), header time,key_name,domain,status,timing_ms,billable. Requires from and to.
Evidence & runs
Latest stored run for a request. Accepts a dashboard token or an API key.
Specific run. run_id is latest, genesis, or a numeric drift run.
Versions ascending by run id: [{ run_id, created_at }]. Use it to build a drift timeline.
Stored runs include websites[].assessments.mcc, websites[].assessments.policy_pages, and websites[].evidences. Newer runs also carry created_at; legacy runs fall back to the version timestamp.
Billing
Current plan, billing period, usage, and next invoice.
| Field | Meaning |
|---|---|
plan.name | Current plan. |
plan.price | Per-check rate in major units; null means custom pricing (Enterprise). |
plan.currency | ISO currency code. |
plan.free_checks | Lifetime allowance, or null when the plan is metered. |
period.from, period.to | Current billing period, epoch ms. |
usage.requests, usage.billable | Runs and billable runs in the period. |
usage.amount | Period spend in major units, excluding GST. |
usage.free_used | Lifetime checks consumed against the allowance, account-level and shared by every API key. |
next_invoice_at | Next invoice date, epoch ms. |
Newest-first invoices: id, number, created_at, amount, currency, status (paid | open | void), and optional url.
Webhooks
Manage delivery endpoints for drift notifications. Adding or updating endpoints requires a verified account with remaining checks; entry plans lose add/update access once the lifetime allowance is exhausted, and deliveries stop. Existing endpoints are never deleted by a plan change, and Delete is always allowed.
Body { "url": "https://..." }. HTTPS only. 400 invalid URL · 403 no access/checks exhausted · 409 duplicate.
Update the URL. Changing it resets health to pending and clears the last-check fields.
Remove an endpoint. Notifications stop immediately.
Sends a synchronous drift.test event and returns { ok, status_code, error }. Gated like add/update.
Issues a new signing secret and returns it once; the previous secret stops working immediately.
Endpoint object
{
"id": 7,
"url": "https://example.com/hooks/beetle",
"status": "healthy",
"last_checked_at": 1762592000000,
"last_status_code": 200,
"last_error": null,
"created_at": 1762500000000
}
status is healthy, failing, or pending (no delivery attempted yet).
Signing
POST /webhooks returns a one-time secret alongside the endpoint. Deliveries carry X-Beetle-Event, X-Beetle-Timestamp, and X-Beetle-Signature — an HMAC-SHA256 of ${timestamp}.${body} using that secret. Compare in constant time and reject stale timestamps.
failing. An account may register up to 10 endpoints. Entry-plan deliveries stop when the lifetime check allowance is exhausted.
Errors
Errors return JSON with error and/or message.
| Status | Meaning |
|---|---|
| 400 | Invalid input: URL, range, interval, or path id. |
| 401 | Missing, invalid, or expired credential. |
| 402 | Payment required; the lifetime free-check allowance is exhausted. |
| 403 | Email not verified, or the plan lacks the requested entitlement. |
| 404 | Unknown id, or an id owned by another account. |
| 409 | Duplicate resource, such as an already-registered webhook URL. |
| 500 | Classification or server failure. Safe to retry. |
Plans & limits
| Plan | Volume band (checks/month) | Rate per check | Entry allowance | History |
|---|---|---|---|---|
| Evaluation | One-time trial | Free | 25 lifetime | 90 days |
| Starter | 1–1,000 | ₹30 | 25 lifetime | 12 months |
| Growth | 1,001–10,000 | ₹20 | None | 12 months |
| Scale | 10,001–100,000 | ₹12 | None | 36 months |
| Enterprise | 100,000+, or by contract | Custom, ₹6 floor | None | Custom |
Volume bands set the rate for the month's successful checks; they are pricing bands, not hard usage caps. A check is billed only when at least one policy page resolves. Prices exclude GST (18%), and there is no monthly minimum commit.
Changelog
- 2026-10-28 — Webhook PATCH; webhook entitlement for Evaluation/Starter, credit-gated. Usage filters, export, and per-request evidence.
- 2026-10-27 — Plan matrix, billing summary and invoices, free-check metadata.
- 2026-10-26 — Usage summary with daily/monthly buckets.
Questions: support@getbeetle.in