Skip to main content

Private API

This API is for external partner platforms. The website itself must use React Router loaders/actions and shared server functions directly, not /api/v1.

This API is currently subject to breaking changes. Treat this documentation as the source of truth; we keep it updated as the project and API contract evolve.

Base URL

https://venezuelatebusca.com/api/v1

Authentication

Every /api/v1 request requires an API key:

Authorization: Bearer <api_key>

API keys are stored as hashes in D1. A key has a source, and that source is used automatically for person provenance. Clients may send source_id and source_notes, but cannot send or override source.

Source handles:

form
hospital_list
persons_list
pacientes_terremoto_venezuela
sos_venezuela_2026
laguaira_hsiciliano

The API documentation only shows the read-oriented surface. Write endpoints remain available for approved partners with explicit permissions and are documented in the private OpenAPI reference.

API keys can have explicit permissions:

persons:read
persons:write
tips:read
tips:write
reports:read
reports:write
resources:read
resources:write

New keys default to read-only access:

persons:read
tips:read
reports:read
resources:read

Disabled keys or missing permissions return 403. Missing or invalid keys return 401.

Rate Limits

Cloudflare Workers Rate Limiting protects the API:

  • Partner API: 120 requests per minute per API key.
  • Auth failures and anonymous attempts: 20 requests per minute per IP.

Rate-limited responses return 429 and include Retry-After.

Responses

Successful non-delete responses always return data.

Single resource:

{
"data": {
"id": "person_123",
"first_name": "Ana"
}
}

List resource:

{
"data": [],
"pagination": {
"next_cursor": null,
"previous_cursor": null,
"limit": 50
}
}

General errors:

{
"error": "Person not found."
}

Validation errors:

{
"errors": {
"first_name": ["First name is required."]
}
}

Pagination

List endpoints use cursor pagination.

  • limit defaults to 50.
  • limit is capped at 100.
  • Use cursor exactly as returned.
  • Do not parse or edit cursors.
  • No page, offset, or total_count.
GET /api/v1/persons?limit=100
GET /api/v1/persons?limit=100&cursor=<opaque_cursor>

When next_cursor is null, there is no next page. When previous_cursor is null, there is no previous page.

Endpoints

GET /api/v1/metrics
requires persons:read

GET /api/v1/persons
requires persons:read
GET /api/v1/persons/:person_id
requires persons:read

GET /api/v1/persons/:person_id/tips
requires tips:read
GET /api/v1/persons/:person_id/tips/:tip_id
requires tips:read

GET /api/v1/persons/:person_id/reports
requires reports:read
GET /api/v1/persons/:person_id/reports/:report_id
requires reports:read

GET /api/v1/resources
requires resources:read
GET /api/v1/resources/:resource_id
requires resources:read

Persons

List filters use stable handles:

GET /api/v1/persons?source=persons_list
GET /api/v1/persons?hospital=miguel_perez_carreno
GET /api/v1/persons?hospital_status=admitted
GET /api/v1/persons?gender=female

Person responses enrich controlled values as { "handle": "...", "label": "..." }. Every person response includes sources as flat resource objects and tips as plain tip objects.

Tips

Tips are scoped by person_id. A tip_id that belongs to a different person returns 404.

Reports

Report reasons:

duplicate
inappropriate
incorrect_info
already_found
other

Reports are scoped by person_id. A report_id that belongs to a different person returns 404.

Metrics

GET /api/v1/metrics
{
"data": {
"total": 123,
"missing": 100,
"found": 23
}
}

Status Codes

400 Bad request or invalid JSON
401 Missing or invalid API key
403 Disabled API key, missing permission, or forbidden source ownership
404 Resource not found
405 Unsupported method
409 Conflict, such as national ID uniqueness
415 Unsupported content type
422 Validation error
429 Rate limited
500 Unexpected server error