Unlocks
Spend one unlock to reveal a single censored record in full. Free tier only; the paid tiers have nothing to reveal.
POST /unlocks
Reveal one censored record
Spend one unlock to reveal a single censored record in full. The grant is permanent: it survives the weekly unlock reset and applies to that record from any later query that reaches it, so a row revealed once stays revealed.
Two things that are easy to get wrong:
- charged: false means the record was already held. Holding it again costs nothing, so a double click or a retry after a dropped response is safe. - An unlock lifts value masking only. It does not open the ai_analysis or ai_chat file-browser tabs, which stay refused below the Premium file-browser entitlement because each is a paid language-model call with no censored form to serve.
Authentication is the same as on the other /api routes. The call writes a grant and reaches no upstream, so it is not metered as a search and carries only the global rate limiter.
What you send.
JSON body, required.
kind string required
Which surface the record came from. The two surfaces address a record differently, so the kind is part of its identity rather than a hint.
One of breach, device
record_id string required
The
record_idthe row carried - a content digest for breach rows, the provider'sstealerlog_idfor device rows.term string
The query the reader was looking at when they spent the unit. Audit trail only; it is never read back into a gate, so it neither widens nor narrows the grant.
query_type string
The search type behind that query. Audit trail only.
What comes back.
200 The record is held. The balance rides along on every outcome, refusals included, so the control that was just pressed can redraw its remaining count without a second call.
success boolean required
data object
unlocks integer
Unlocks spent in the current unlock period, counted after this call.
unlocks_limit integer
Unlock allowance for the unlock period; 5 a week on the free tier.
kind string
One of breach, device
record_id string
unlocked boolean
charged boolean
Whether this call spent a unit.
falsemeans the record was already held, which costs nothing, so a double click or a retry after a dropped response is safe.
400 The body would not parse, `kind` is neither `breach` nor `device`, or `record_id` is empty or longer than 128 characters.
success boolean required
error string required
code string
Machine-readable error tag. Omitted whenever it is empty, which is most generic errors, so branch on status too.
One of quota_exceeded, hourly_limit, file_access_limit, plan_upgrade_required, upgrade_required, feature_not_enabled, email_verification_required, monitor_limit, monitor_surface_not_allowed, monitor_first_seen_not_allowed, monitor_webhook_invalid, monitor_webhook_not_allowed, monitor_webhook_key_required
401 The request carried no valid session or API key.
The same shape as 400.
403 The plan has no unlock mechanism at all. Every paid tier answers this way: paid rows are not censored, and Pro's masked device browser is the Premium upsell rather than something to spend units on. The code is the same `plan_upgrade_required` the device gates send.
The same shape as 400.
429 The unlock allowance is spent. It refills on its own weekly clock, reported as `unlocks_reset_at` by `GET /search/stats`, rather than with the search counters. Unlike the other quota refusals this one still carries `data`, with `unlocks` and `unlocks_limit`, so the caller can redraw the balance without a second request.
success boolean required
error string required
code string
Machine-readable error tag. Omitted whenever it is empty, which is most generic errors, so branch on status too.
One of quota_exceeded, hourly_limit, file_access_limit, plan_upgrade_required, upgrade_required, feature_not_enabled, email_verification_required, monitor_limit, monitor_surface_not_allowed, monitor_first_seen_not_allowed, monitor_webhook_invalid, monitor_webhook_not_allowed, monitor_webhook_key_required
data object
unlocks integer
unlocks_limit integer