Harm Watch

API documentation

The Harm Watch provides a read-only JSON API for approved organisations. Access is granted per organisation — apply here.

Authentication

Include your API key as a Bearer token in every request:

Authorization: Bearer hw_live_your_key_here

Keys are issued by your organisation admin from the API keys page. A revoked or expired key returns 401.

Rate limits

Default: 1,000 requests per hour per key. Every response includes:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1722614400   # Unix timestamp

Exceeding the limit returns 429 Too Many Requests with a Retry-After header.

Endpoints

GEThttps://www.harm.watch/api/v1/submissions

Paginated list of submissions. Defaults to status=live.

Query parameters

statuslive (default). Comma-separated for multiple: live,pending_review
categoryFilter by category slug, e.g. fraud
descriptorFilter by descriptor slug
domainExact domain match, e.g. example.com
sinceISO 8601 date — only records with last_reported_at after this
cursorOpaque pagination cursor from next_cursor in prior response
limitRecords per page, default 100, max 500

Response

{
  "data": [
    {
      "id": "uuid",
      "url": "https://example.com/path",
      "domain": "example.com",
      "status": "live",
      "categories": ["fraud", "impersonation"],
      "descriptors": ["targets-children"],
      "report_count": 7,
      "first_reported_at": "2026-01-04T09:12:00Z",
      "last_reported_at": "2026-02-11T18:40:00Z",
      "status_changed_at": null
    }
  ],
  "next_cursor": "eyJ0cyI6...",
  "has_more": true
}
GEThttps://www.harm.watch/api/v1/submissions/:id

Single submission by UUID. Same shape as a list item.

Returns 404 if not found.

GEThttps://www.harm.watch/api/v1/tags

Active harm taxonomy — categories and descriptors. Use this to map slugs to human-readable labels.

{
  "data": [
    {
      "id": "uuid",
      "slug": "fraud",
      "label": "Fraud, scams, and financial crime",
      "kind": "category",
      "description": null,
      "parent_id": null,
      "sort_order": 1
    }
  ]
}

Errors

All errors use a consistent envelope:

{
  "error": {
    "code": "rate_limited",
    "message": "You have exceeded your rate limit. Try again after the Retry-After period."
  }
}
401unauthorizedMissing, invalid, or revoked API key
403forbiddenAPI access not enabled for this organisation
404not_foundResource not found
429rate_limitedRate limit exceeded
500server_errorInternal error — please try again

Data notice

All records are allegations by members of the public, not confirmed findings. Never characterise a submission as proven, confirmed, or illegal. Reporter data (email addresses, report text, IP information) is never returned by the API.