API Reference

The API behind the research path: 27 federal sources and 3M+ resolved US entities. Authenticate with an API key, query by name or identifier (CIK, EIN, LEI, DUNS, UEI, NPI, CRD), stream events, or ask in natural language.

Authentication

All API requests require an API key passed in the X-API-Key header. Keys use the format pt_live_ followed by your unique key string.

You can generate and manage API keys from your dashboard. Keep your keys secure and never expose them in client-side code.

Authentication Header
# Include your API key on each request
curl https://api.publictrace.dev/v1/entity/search?q=Acme \
  -H "X-API-Key: pt_live_your_key_here"

Base URL

All API endpoints are served from a single base URL. Use HTTPS for all requests.

Base URL
https://api.publictrace.dev/v1

Response Format

All responses are returned as JSON with a consistent envelope structure. Successful responses include a data field, a meta field with request metadata, and a pagination field for list endpoints.

Response Envelope
{
  "data": [ ... ],
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2025-03-15T10:30:00Z",
    "credits_used": 1,
    "credits_remaining": 4999
  },
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 142,    // null on fuzzy-search endpoints (count too costly)
    "has_more": true
  }
}

Interactive Reference

This page is the curated, narrative reference. For a complete, always-current machine reference generated directly from the running API, use the interactive OpenAPI surfaces:

Swagger UI /api/swagger Try endpoints in the browser. Click Authorize and paste your X-API-Key to send live, authenticated requests.
ReDoc /api/redoc Clean three-pane reference for reading the full schema, models, and field-level detail.
OpenAPI schema /api/openapi.json The raw OpenAPI 3 document. Import into Postman, Insomnia, or generate a client SDK. Also served at /openapi.json for tools that default to the root.
GET /v1/entity/{entity_id}

Retrieve the full entity profile, including all known identifiers, source coverage, and aggregate statistics across all federal data sources.

Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": {
    "entity_id": "ent_e36d8ec996d299a5",
    "name": "AMAZON COM INC",
    "state": "WA",
    "city": "SEATTLE",
    "sic_code": "5961",
    "naics_code": null,
    "identifiers": [
      {"type": "cik", "value": "0001018724"},
      {"type": "ein", "value": "91-1646860"},
      {"type": "lei", "value": "ZXTILKJKG63JELOEG630"}
    ],
    "alternate_names": ["AMAZON.COM, INC.", "AMAZON.COM SERVICES, INC.", "Amazon"],
    "sources": ["sec", "sec_13dg", "lda", "fjc_civil", "osha_inspection", "dol_whd", "gleif"],
    "record_count": null,
    "subsidiary_count": null,
    "source_summary": [],
    "role": null,
    "record_counts": {
      "sec": 109, "fjc_civil": 1430, "dol_whd": 24, "osha_inspection": 27
      // …one count per source
    },
    "source_records": [
      { "source": "sec", "record_type": "filing", "record_date": "2026-04-14", "title": "8-K filing" }
      // …most-recent records across sources
    ],
    "hq": {
      "address": "410 TERRY AVENUE NORTH", "city": "SEATTLE", "state": "WA",
      "country": "US", "postal": "98109", "source": "sec_edgar_submissions", "as_of": null
    },
    "sanction_summary": null,
    "financials": {
      "revenue_usd": 716924000000, "net_income_usd": 77670000000
      // …assets, debt, employees + period_end fields
    },
    "risk_score": { "score": 34, "tier": "medium", "methodology_version": "v3", "computed_at": "2026-05-31T03:56:35Z" },
    "created_at": "2026-05-02T00:38:29Z",
    "updated_at": "2026-05-08T03:58:16Z"
  },
  "meta": { "request_id": "req_5fb9eb7254fb", "credits_used": 1, "credits_remaining": 19940 },
  "pagination": null
}
GET /v1/entity/{entity_id}/violations

Retrieve violations, citations, and enforcement actions for a specific entity. Includes OSHA citations, EPA violations, FDA recalls, FTC enforcement, and SAM.gov exclusions.

Parameters

source string Filter by source: osha, epa, fda, ftc, sam.
date_from string Start date filter (ISO 8601, e.g., 2024-01-01).
date_to string End date filter (ISO 8601).
min_penalty number Minimum penalty amount in USD.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/violations?source=osha&min_penalty=10000" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "record_id": "osha_violation_osha_vio_347970709_01001",
      "entity_id": "ent_e36d8ec996d299a5",
      "source": "osha_violation",
      "violation_type": null,
      "description": "OSHA Other violation (citation 01001)",
      "violation_date": "2025-06-13",
      "penalty_amount": null,
      "status": null,
      "url": "https://www.osha.gov/ords/imis/InspectionNr.search?id=347970709",
      "url_label": "OSHA inspection detail",
      "data": {
        "_state": "VA", "viol_type": "O", "activity_nr": "347970709", "citation_id": "01001"
        // …source-specific fields
      },
      "created_at": "2026-05-11T23:53:53Z"
    }
  ],
  "meta": { "request_id": "req_c8f8d72bbb16", "credits_used": 1, "credits_remaining": 19939 },
  "pagination": { "page": 1, "per_page": 25, "total": 25, "has_more": true }
}
GET /v1/entity/{entity_id}/filings

Retrieve filings and regulatory submissions for an entity. Includes SEC EDGAR filings, IRS 990 forms, and other regulatory documents.

Parameters

source string Filter by source: sec, irs.
filing_type string Filing type code (e.g., 10-K, 10-Q, 8-K, 990).
date_from string Start date filter (ISO 8601).
date_to string End date filter (ISO 8601).
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/filings?source=sec&filing_type=10-K" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "record_id": "sec_sec_filing_0001104659-26-042880",
      "entity_id": "ent_e36d8ec996d299a5",
      "source": "sec",
      "filing_type": "8-K",
      "filing_date": "2026-04-14",
      "period_of_report": "2026-04-14",
      "description": "Form 8-K - Current report - Item 7.01 Item 9.01",
      "url": "https://www.sec.gov/Archives/edgar/data/1018724/000110465926042880/",
      "url_label": "SEC 8-K filing",
      "data": {
        "cik": "0001018724", "form_type": "8-K", "accession_no": "0001104659-26-042880"
        // …items, entities, resources, etc.
      },
      "created_at": "2026-05-05T05:45:11Z"
    }
  ],
  "meta": { "request_id": "req_df403f187456", "credits_used": 1, "credits_remaining": 19938 },
  "pagination": { "page": 1, "per_page": 25, "total": 109, "has_more": true }
}
GET /v1/entity/{entity_id}/timeline

Retrieve a unified, chronological timeline of all events for an entity across all sources. Events include filings, violations, recalls, enforcement actions, and exclusions.

Parameters

source string Filter by source.
date_from string Start date filter (ISO 8601).
date_to string End date filter (ISO 8601).
include_filings bool Include routine SEC filing events in the timeline. Default false.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/timeline?date_from=2024-01-01" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "event_id": "dol_whd_5090392",
      "entity_id": "ent_e36d8ec996d299a5",
      "event_type": "labor_violation",
      "source": "dol_whd",
      "event_date": "2026-05-11",
      "title": "Amazon.com, Inc.",
      "description": "Labor violation",
      "url": null,
      "url_label": null,
      "metadata": {
        "city": "Amarillo", "state": "TX", "case_id": "5090392", "naics_code": "455219"
        // …source-specific fields
      }
    }
  ],
  "meta": { "request_id": "req_d5e75b557b68", "credits_used": 1, "credits_remaining": 19937 },
  "pagination": { "page": 1, "per_page": 25, "total": 1732, "has_more": true }
}
GET /v1/entity/{entity_id}/sanctions

Retrieve OpenSanctions-aggregated list memberships for an entity (OFAC SDN, EU FSF, UN SC, US SAM Exclusions, and 100+ other upstream watchlists), kept separate from /violations so foreign sanction listings don't inflate a regulatory penalty count. Costs 1 credit.

Parameters

topic string Filter by OpenSanctions topic code: sanction, debarment, export.control, crime.terror, reg.action, etc.
dataset string Filter by upstream dataset: us_ofac_sdn, eu_fsf_sanctions, un_sc_sanctions, us_sam_exclusions, etc.
date_from string Start date filter on the record date (ISO 8601, e.g. 2024-01-01).
date_to string End date filter on the record date (ISO 8601).
page number Page number. Defaults to 1.
per_page number Results per page, 1-100. Defaults to 25.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/sanctions?topic=sanction" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "record_id": "rec_9a1f02",
      "entity_id": "ent_321d75",
      "source": "sanctions",
      "title": "ACME LTD — flagged for sanction (us_ofac_sdn)",
      "record_date": "2023-08-14",
      "topics": ["sanction"],
      "datasets": ["us_ofac_sdn"],
      "countries": ["ru"],
      "first_seen": "2023-08-14T00:00:00+00:00",
      "last_seen": "2026-05-01T00:00:00+00:00",
      "last_change": "2025-02-10T00:00:00+00:00",
      "url": "https://sanctionssearch.ofac.treas.gov/Details.aspx?id=12345",
      "url_label": "OFAC SDN entry",
      "created_at": "2023-08-15T03:21:00+00:00",
      "data": null
    }
  ],
  "pagination": { "page": 1, "per_page": 25, "total": 1, "has_more": false }
}
GET /v1/entity/{entity_id}/inspections

Retrieve OSHA inspection records for an entity, including inspections that produced no citation. Each row carries inspection-level fields plus an aggregated child-violation count and total penalty computed on read. Costs 1 credit.

Parameters

date_from string Start date filter on the inspection open date (ISO 8601).
date_to string End date filter (ISO 8601).
insp_type string OSHA inspection type code: F (fatality/catastrophe), G (complaint), I (referral), L (planned), M (monitoring), O (other), R (referral), S (selected).
page number Page number. Defaults to 1.
per_page number Results per page, 1-100. Defaults to 25.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/inspections?insp_type=L" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "record_id": "rec_44ab10",
      "entity_id": "ent_321d75",
      "source": "osha_inspection",
      "activity_nr": "317492051",
      "open_date": "2024-03-11",
      "close_case_date": "2024-07-02",
      "site_address": "1200 Industrial Pkwy",
      "site_city": "Wichita",
      "site_state": "KS",
      "site_zip": "67209",
      "naics_code": "336411",
      "insp_type": "L",
      "insp_scope": "C",
      "owner_type": "A",
      "safety_hlth": "S",
      "nr_in_estab": "850",
      "violation_count": 3,
      "total_penalty_usd": 28500,
      "url": "https://www.osha.gov/ords/imis/establishment.inspection_detail?id=317492051",
      "url_label": "OSHA inspection detail",
      "data": {
        "_state": "KS", "activity_nr": "317492051", "naics_code": "336411", "mail_address": "1200 Industrial Pkwy"
        // …source-specific fields
      },
      "created_at": "2024-08-01T11:05:00+00:00"
    }
  ],
  "pagination": { "page": 1, "per_page": 25, "total": 1, "has_more": false }
}
GET /v1/entity/{entity_id}/risk-score

Retrieve the precomputed risk score for an entity. Methodology v3 is anchor-floor: a binding sanction floors the score at 95, a federal debarment/exclusion at 85, and the remaining points stack from per-dimension and per-source signals. Tiers: critical (80-100), high (50-79), medium (20-49), low (0-19). The score is informational, not a compliance clearance. Free (0 credits).

Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/risk-score" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": {
    "entity_id": "ent_e36d8ec996d299a5",
    "name": "AMAZON COM INC",
    "score": 34,
    "tier": "medium",
    "methodology_version": "v3",
    "computed_at": "2026-05-31T03:56:35Z",
    "decay_window_years": 10,
    "breakdown": {
      "anchor": {
        "basis": null,
        // basis is "binding_sanction", "federal_exclusion", or null
        "floor": 0,
        "datasets": [],
        "stack_points": 33.83
      },
      // per-source blocks are keyed by source name directly:
      "dol_whd": {
        "case_count": 24, "raw_points": 5, "most_recent": "2025-04-01",
        "decay_factor": 0.884, "decayed_points": 4.42, "total_back_wages": 105061.93
      },
      // …fda, fjc_civil, osha_violation, epa_icis_fec, sec_enforcement, …
      "by_dimension": {
        "workplace": { "sources": ["osha_violation", "dol_whd"], "raw_points": 10.0, "decayed_points": 8.94 },
        "enforcement": { "sources": ["fjc_civil", "fda"], "raw_points": 38.0, "decayed_points": 24.9 }
        // …sanctions, environmental
      },
      "penalty_magnitude": { "raw_points": 0, "total_penalties_usd": 0.0 }
    },
    "sources_consulted": ["dol_whd", "epa_icis_fec", "fda", "fjc_civil", "osha_violation", "sec_enforcement"],
    "disclaimer": "Informational risk indicator from PublicTrace's public-record corpus..."
  },
  "pagination": null
}
GET /v1/entity/{entity_id}/orphans

Find records in the data lake whose name resembles the entity's canonical name or aliases but could not be deterministically attributed to it — surfaced as unverified enrichment context, explicitly NOT linked in the identifier graph. Costs 1 credit.

Parameters

subject string Restrict to one record subject category: all, company, person, vessel, aircraft, other. Defaults to all (which excludes persons).
page number Page number. Defaults to 1.
per_page number Results per page, 1-100. Defaults to 25.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/orphans?subject=company" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "orphan_id": 8841920,
      "source": "osha_inspection",
      "record_type": "inspection",
      "record_subject": "company",
      "name": "Boeing Wichita Inc",
      "state": "KS",
      "city": "Wichita",
      "address": "3801 S Oliver St",
      "country": "US",
      "record_date": "2022-09-14",
      "severity": null,
      "description": "Planned safety inspection",
      "score": 0.35,
      "topics": null,
      "datasets": null,
      "url": "https://www.osha.gov/ords/imis/establishment.inspection_detail?id=...",
      "url_label": "OSHA inspection"
    }
  ],
  // total is null on this fuzzy-search endpoint
  "pagination": { "page": 1, "per_page": 25, "total": null, "has_more": false }
}
POST /v1/entity/{entity_id}/flag

Submit a "this entity looks wrong" report against an entity. Idempotent per caller: re-flagging the same entity returns the existing row with already_flagged: true. Free (0 credits).

Request Body

section string Optional. The explorer tab the user was on when flagging, for admin debug context. One of profile, tree, subsidiaries, filings, violations, sanctions, orphans, timeline, benchmark, risk, ask. Unrecognized values are dropped.
Example Request
curl "https://api.publictrace.dev/v1/entity/ent_321d75/flag" \
  -H "X-API-Key: pt_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"section": "violations"}'
Example Response
{
  "data": {
    "id": 4192,
    "entity_id": "ent_321d75",
    "status": "open",
    "created_at": "2026-05-30T14:22:00+00:00",
    "already_flagged": false
  }
}
GET /v1/search/uncatalogued

Expand one uncatalogued (orphan) cluster from a /v1/search result into its underlying records. Pass back the exact name_normalized + subject cluster key (an exact match, not fuzzy). Costs 1 credit.

Parameters

name_normalized string Required. The name_normalized key from a /v1/search uncatalogued result. 2-160 characters.
subject string Required. The record_subject from the uncatalogued result: company, person, vessel, aircraft, or other.
page number Page number. Defaults to 1.
per_page number Results per page, 1-100. Defaults to 25.
Example Request
curl "https://api.publictrace.dev/v1/search/uncatalogued?name_normalized=acme+roofing&subject=company" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "orphan_id": 2210045,
      "source": "dol_whd",
      "record_type": "case",
      "record_subject": "company",
      "name": "Acme Roofing LLC",
      "state": "TX",
      "city": "Austin",
      "address": "450 Congress Ave",
      "country": "US",
      "record_date": "2023-06-01",
      "severity": null,
      "description": "FLSA back-wages case",
      "score": 1.0,
      "topics": null,
      "datasets": null,
      "url": "https://enforcedata.dol.gov/views/data_summary.php",
      "url_label": "DOL WHD case"
    }
  ],
  "pagination": { "page": 1, "per_page": 25, "total": null, "has_more": false }
}
GET /v1/alerts

List triggered alerts for your account. Returns recent alert events for watched entities, ordered by most recent first.

Add ?narrative=true to enrich alert events with AI-generated explanations. Base cost: 1 credit. With narrative: 3 credits.

Parameters

since string Return alerts since this ISO 8601 datetime.
entity_id string Filter alerts to a specific entity.
narrative bool If true, each event gets an extra narrative object (AI-generated explanation). Default false. Raises cost to 3 credits.
page integer Page number. Default 1.
per_page integer Results per page (max 100). Default 25.
Example Request
curl "https://api.publictrace.dev/v1/alerts?since=2025-03-01T00:00:00Z" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "record_id": "debarment_NK-GvNa8teaWadLgnrdFriAKc",
      "entity_id": "ent_466ea17d7311f558",
      "entity_name": "BOEING CO",
      "event_type": "entity.event.created",
      "source": "debarment",
      "title": "orphan-promoted: The Boeing Company",
      "event_date": "2025-09-11",
      "detail_url": "/v1/entity/ent_466ea17d7311f558"
    }
  ],
  "meta": { "request_id": "req_3b72ebe4157e", "credits_used": 1, "credits_remaining": 19933 },
  "pagination": { "page": 1, "per_page": 25, "total": 39159, "has_more": true }
}
POST /v1/alerts/watch

Start watching an entity for new events. When a matching event occurs, a webhook will be sent to your configured URL.

Request Body

entity_id required string The entity ID to watch.
event_types array Event types to watch: entity.violation.created, entity.filing.created, entity.event.created, etc. Default: all.
webhook_url string Optional. HTTPS URL to receive webhook POST payloads. If omitted, the watch is in-app only.
Example Request
curl -X POST "https://api.publictrace.dev/v1/alerts/watch" \
  -H "X-API-Key: pt_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_id": "ent_321d75",
    "event_types": ["entity.violation.created", "entity.filing.created"],
    "webhook_url": "https://your-app.com/webhooks/publictrace"
  }'
Example Response
{
  "data": {
    "id": 221,
    "customer_id": 17,
    "entity_id": "ent_321d75",
    "event_types": ["entity.violation.created", "entity.filing.created"],
    "webhook_url": "https://your-app.com/webhooks/publictrace",
    "created_at": "2025-03-15T10:00:00Z"
  }
}
DELETE /v1/alerts/watch/{entity_id}

Stop watching an entity. Removes all webhook subscriptions for the specified entity under your account.

Example Request
curl -X DELETE "https://api.publictrace.dev/v1/alerts/watch/ent_321d75" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": {
    "deleted": true,
    "entity_id": "ent_321d75"
  }
}
GET /v1/alerts/watches

List the entities on the caller's watchlist, including watches whose entities have had no events yet (unlike /v1/alerts, which only returns entities with recent events). Each row carries the joined entity name/location plus a count of new events since the watch was last marked seen. Free (0 credits).

Example Request
curl "https://api.publictrace.dev/v1/alerts/watches" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "entity_id": "ent_321d75",
      "entity_name": "Acme Manufacturing Inc",
      "state": "KS",
      "city": "Wichita",
      "event_types": ["entity.violation.created"],
      "created_at": "2026-05-01T09:00:00+00:00",
      "last_seen_at": "2026-05-29T18:30:00+00:00",
      "new_events_count": 2,
      "last_event_at": "2026-05-30T07:12:00+00:00"
    }
  ]
}
POST /v1/alerts/watches/mark-seen

Bulk-update last_seen_at to now for all of the caller's watches, resetting the per-row new-events badge counts. Idempotent, takes no body. Free (0 credits).

Example Request
curl -X POST "https://api.publictrace.dev/v1/alerts/watches/mark-seen" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": { "updated": 5 }
}
GET /v1/alerts/webhook-secret

Report the status of the caller's webhook signing secret — whether one is on file and its masked prefix. The full secret is never returned here (only at rotation time). Free (0 credits). Note: this endpoint returns a flat object, not the standard data envelope.

Example Request
curl "https://api.publictrace.dev/v1/alerts/webhook-secret" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "has_secret": true,
  "masked": "pt_whsec_ab…",
  "rotated_at": "2026-05-10T12:00:00+00:00"
}
POST /v1/alerts/webhook-secret/rotate

Generate a new webhook signing secret and return the FULL value — this is the only time it is sent over the wire. Rotation is destructive: any endpoint still validating against the old secret starts failing immediately. Takes no body. Free (0 credits). Returns a flat object, not the standard data envelope.

Example Request
curl -X POST "https://api.publictrace.dev/v1/alerts/webhook-secret/rotate" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "secret": "pt_whsec_ab12cd34ef56gh78ij90kl",
  "rotated_at": "2026-05-30T14:30:00+00:00",
  "warning": "This is the only time the full secret is shown. Save it now. Subsequent reads return only the masked prefix."
}
GET /v1/alerts/deliveries

List recent webhook delivery attempts for the caller's watches, with HTTP status, attempt count, and any last error. Free (0 credits). Returns a flat object keyed on deliveries, not the standard data envelope.

Parameters

watch_id number Filter to deliveries for a single watch.
status string Filter by status: pending, sent, failed, gave_up.
limit number Max rows to return, 1-500. Defaults to 50.
Example Request
curl "https://api.publictrace.dev/v1/alerts/deliveries?status=failed" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "deliveries": [
    {
      "id": 9013,
      "watch_id": 221,
      "record_id": "rec_44ab10",
      "entity_id": "ent_321d75",
      "webhook_url": "https://hooks.example.com/publictrace",
      "source": "osha_violation",
      "record_type": "violation",
      "title": "Serious citation issued",
      "status": "failed",
      "attempt_n": 3,
      "http_status": 500,
      "last_error": "HTTP 500 from endpoint",
      "requested_at": "2026-05-30T07:12:30+00:00",
      "completed_at": "2026-05-30T07:12:31+00:00",
      "next_retry_at": "2026-05-30T07:42:00+00:00"
    }
  ]
}
POST /v1/alerts/deliveries/{delivery_id}/retry

Mark a webhook delivery as pending and due now, resetting its attempt counter so it gets the full retry allowance again. The delivery worker picks it up on its next tick. Takes no body. Free (0 credits). Returns a flat object, not the standard data envelope.

Example Request
curl -X POST "https://api.publictrace.dev/v1/alerts/deliveries/9013/retry" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "id": 9013,
  "status": "pending"
}
POST /v1/alerts/test-fire

Send a synthetic test.ping event to a webhook URL so you can verify your endpoint accepts the signature scheme. POSTs synchronously and returns the immediate result; not recorded in deliveries. Free (0 credits). Returns a flat object, not the standard data envelope.

Request Body

url string Optional. The URL to test-fire. If omitted, the most recent watch carrying a webhook_url is used; if none exists, the request 400s.
Example Request
curl "https://api.publictrace.dev/v1/alerts/test-fire" \
  -H "X-API-Key: pt_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://hooks.example.com/publictrace"}'
Example Response
{
  "ok": true,
  "http_status": 200,
  "url": "https://hooks.example.com/publictrace",
  "event_id": "evt_test_1748614200"
}
Verifying webhook signatures

Each webhook we POST to your webhook_url is signed with your account's webhook secret — the pt_whsec_… value shown once when you rotate it on the Webhooks page. Verify the signature on each delivery before trusting the payload.

The signature header

We send a header in Stripe's format:

Header
X-PublicTrace-Signature: t=1748900000,v1=3a7f9c2b…e9
t unix seconds Timestamp when we signed the payload.
v1 hex HMAC-SHA256 of "<t>." + raw_body, keyed with your secret.

How to verify

Recompute the HMAC over the timestamp and raw body, then compare it constant-time against v1. Three things to get right:

Use the raw body Sign the exact bytes you received, before any JSON parse/re-encode. Re-serializing changes key order/whitespace and breaks the signature.
Sign "t." + body The timestamp is part of the signed message (replay defense), not just a sibling header.
Reject stale + compare constant-time Reject deliveries where t is more than 300 seconds old, and use a timing-safe comparison.
Python (Flask / FastAPI)
import hmac, hashlib, time

def verify(secret, raw_body, sig_header, max_age=300):
    parts = dict(p.split("=", 1) for p in sig_header.split(",") if "=" in p)
    try:
        ts = int(parts.get("t", ""))
    except ValueError:
        return False
    if not parts.get("v1") or abs(int(time.time()) - ts) > max_age:
        return False
    msg = f"{ts}.".encode() + raw_body          # raw bytes, not re-encoded JSON
    expected = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

# Flask: raw_body = request.get_data()  (NOT request.json)
Node (Express)
const crypto = require("crypto");

function verify(secret, rawBody, sigHeader, maxAge = 300) {
  const p = Object.fromEntries(sigHeader.split(",").map(s => s.split("=")));
  const ts = parseInt(p.t, 10);
  if (!ts || Math.abs(Date.now() / 1000 - ts) > maxAge) return false;
  const expected = crypto.createHmac("sha256", secret)
    .update(`${ts}.`).update(rawBody).digest("hex");      // rawBody = Buffer
  const a = Buffer.from(expected), b = Buffer.from(p.v1 || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// app.use(express.raw({ type: "application/json" }))  -> req.body is the raw Buffer
Go
func verify(secret string, rawBody []byte, sigHeader string, maxAge int64) bool {
    var ts int64
    var v1 string
    for _, part := range strings.Split(sigHeader, ",") {
        kv := strings.SplitN(part, "=", 2)
        if len(kv) == 2 && kv[0] == "t" { ts, _ = strconv.ParseInt(kv[1], 10, 64) }
        if len(kv) == 2 && kv[0] == "v1" { v1 = kv[1] }
    }
    now := time.Now().Unix()
    if ts == 0 || now-ts > maxAge || ts-now > maxAge { return false }
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(strconv.FormatInt(ts, 10) + "."))
    mac.Write(rawBody)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(v1))
}

Confirm your handler with the Test fire button on the Webhooks page — it sends a signed test.ping through the exact same scheme.

GET /v1/me/audit-log

Read your team's AI-call history (prompts, responses, token counts, latency) for the /v1/ask and AI-narrative endpoints. Pro tier or above; lower tiers receive a 403. Cursor-paginated on descending id. Free (0 credits).

Parameters

since string ISO-8601 lower bound on created_at. Defaults to 7 days ago.
until string ISO-8601 upper bound on created_at. Defaults to now.
endpoint string Filter to a single endpoint path.
cursor number Page from rows with id less than this value. Use the next_cursor from the previous page.
limit number Rows per page, 1-200. Defaults to 50.
Example Request
curl "https://api.publictrace.dev/v1/me/audit-log?limit=50" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": [
    {
      "id": 71204,
      "request_id": "req_a91f02cd",
      "endpoint": "/v1/ask",
      "prompt": "How many OSHA violations does Acme have?",
      "response": "Acme Manufacturing Inc has 8 OSHA violations on record.",
      "input_tokens": 1820,
      "output_tokens": 142,
      "cached": true,
      "latency_ms": 940,
      "created_at": "2026-05-30T13:01:00+00:00"
    }
  ],
  "pagination": { "limit": 50, "next_cursor": 71155 }
}
GET /v1/me/notifications

Read or update your account's notification preferences (email-digest cadence and webhook delivery toggle). Use GET to retrieve and PUT to update. Free (0 credits).

Example Request (GET)
curl "https://api.publictrace.dev/v1/me/notifications" \
  -H "X-API-Key: pt_live_your_key_here"
Example Response
{
  "data": {
    "email_cadence": "daily",
    "webhook_enabled": true,
    "last_email_digest_at": "2026-05-30T06:00:00+00:00",
    "webhook_configured": true
  }
}
PUT /v1/me/notifications

Partial update — only the fields you send are changed; unset fields keep their current value. Flipping a channel from off to on resets its cursor so the first digest/delivery covers only events after the flip.

Request Body

email_cadence string Optional. Email digest cadence: off, daily, or weekly. Any other value returns 422.
webhook_enabled boolean Optional. Enable or disable webhook delivery of alert events.
Example Request (PUT)
curl -X PUT "https://api.publictrace.dev/v1/me/notifications" \
  -H "X-API-Key: pt_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"email_cadence": "weekly", "webhook_enabled": true}'
Example Response
{
  "data": {
    "email_cadence": "weekly",
    "webhook_enabled": true,
    "last_email_digest_at": "2026-05-30T06:00:00+00:00",
    "webhook_configured": true
  }
}

AI & Analytics

PublicTrace offers AI and analytics endpoints that work across 27 federal sources. AI endpoints (Ask, Summary) use LLM analysis and cost 3 credits per call. Analytics endpoints (Benchmark, Corporate Tree) use SQL and cost 1 credit. SEC Facts costs 1 credit for XBRL data or 3 credits with AI extraction. Responses are cached for 24 hours — cache hits don't consume credits.

Natural Language Queries

Use case: A compliance analyst needs to find all companies in California with OSHA penalties above $100K in the last year. Instead of building a complex query, they type the question in plain English.

AI translates the question into structured API parameters, executes the query, and returns formatted results with source attribution.

Entity Risk Summary

Use case: A procurement team is onboarding a new vendor and needs a quick risk overview before the approval meeting.

AI reads the entity's complete record — violations, filings, sanctions, complaints — across all sources and generates a structured JSON briefing with critical flags, penalty totals, and timeline highlights.

Competitive Benchmark

Use case: A risk analyst wants to know if a company's violation rate is normal for its industry.

Returns SIC-peer percentiles for OSHA, EPA, and DOL counts. Deterministic SQL, no LLM.

Corporate Tree

Use case: Due diligence requires understanding parent/subsidiary relationships before approving a deal.

Walks the ownership graph built from SEC Exhibit 21 subsidiaries, SEC 13D/G beneficial ownership, and GLEIF parent/child relationships. SQL, no LLM.

SEC Fact Extraction

Use case: An analyst wants key financial metrics from a company's latest 10-K without reading the full filing.

Extracts structured facts from SEC XBRL filings. Optional local LLM extraction for complex data — no external API cost.

POST /v1/ask

Translate a natural language question into structured API queries and return results. Costs 3 credits.

Request Body

query (string, required) — Natural language question, 1–1000 characters.

entity_id (string, optional) — Focal PublicTrace entity. Include this when the question is asked from an entity profile so pronouns such as “it” and “this company” resolve correctly.

entity_name (string, optional) — Display name for the focal entity. Send it with entity_id to give the model readable context.

Request
POST /v1/ask
X-API-Key: pt_live_xxx
Content-Type: application/json

{
  "query": "What are its largest OSHA penalties?",
  "entity_id": "ent_466ea17d7311f558",
  "entity_name": "The Boeing Company"
}
Response
// The response is wrapped in the standard { data, meta } envelope.
// results[] rows are the RAW columns of whatever query the engine ran,
// so the keys vary by question; each row carries an enriched _entity block.
// match_confidence: "high" | "low" | "none" | "n/a".
{
  "data": {
    "interpretation": "Companies with OSHA penalties over $100,000 in Texas during 2024",
    "match_confidence": "n/a",
    "api_calls_made": [
      { "endpoint": "sql", "description": "Database query", "result_count": 12, "error": null }
    ],
    "results": [
      {
        "entity_id": "ent_a1b2c3",
        "name": "Gulf Coast Manufacturing Inc",
        "violation_count": 3,
        "total_penalty": 287500,
        "_entity": { "name": "Gulf Coast Manufacturing Inc", "state": "TX" /* …identifiers, hq */ }
      }
    ]
  },
  "meta": { "request_id": "req_…", "credits_used": 3, "credits_remaining": 4997 }
}
GET /v1/entity/{entity_id}/summary

Generate an AI risk briefing for an entity. Costs 3 credits. Cached for 24 hours.

All numeric fields (violations, sanctions_and_exclusions, filings, etc.) are computed directly from the underlying records and are exact. The model only writes the prose fields (summary, critical_flags, timeline_highlight) from those figures — it never invents a number. total_count / total_penalties_usd count enforcement actions only; mandatory self-reports (OSHA injury logs, EPA TRI releases) are tracked separately under self_reports.

Request
GET /v1/entity/ent_321d75/summary
X-API-Key: pt_live_xxx
Response
{
  "company_name": "Johnson & Johnson",
  "summary": "Appears in 9 federal sources; 62 enforcement records totaling $890,000 in penalties, concentrated in FDA and OSHA.",
  "critical_flags": ["Large enforcement penalties ($890,000 total)"],
  "sources_found": ["epa_echo", "fda", "irs_990", "osha_violation", "sec"],
  "violations": {
    "total_count": 62,
    "total_penalties_usd": 890000,
    "by_source": {
      "fda": { "count": 14, "latest_date": "2025-03-15", "severity_breakdown": { "high": 3, "medium": 11 } },
      "osha_violation": { "count": 48, "total_penalty": 890000, "latest_date": "2025-01-22" }
    }
  },
  "self_reports": {
    "osha_injury_reports": { "count": 31, "latest_date": "2024-12-01" }
  },
  "filings": {
    "sec": { "latest_filing": "2025-04-30", "filing_types": ["8-K", "10-Q", "10-K"] }
  },
  "sanctions_and_exclusions": {
    "ofac": { "sanctioned": false, "programs": [] },
    "sam_gov": { "excluded": false },
    "hhs_oig": { "excluded": false, "exclusion_types": [] }
  },
  "timeline_highlight": "Most recent record: FDA enforcement action (fda, 2025-03-15)."
}
GET /v1/entity/{entity_id}/benchmark

Compare an entity's compliance profile against its industry peers. The peer group is the set of entities sharing the entity's NAICS code (falling back to SIC code when NAICS is absent); ranks are computed with SQL window functions. Pure SQL — no AI involved. Costs 1 credit.

Each metric reports the entity's own value, the industry median/average (computed over reporting peers only, so a silent majority of zero-record peers doesn't drag it to 0), a reporter_count (how many peers have any record of that type), a percentile rank (0–100), and a rating. Enforcement findings (violations) are kept separate from mandatory self-reports (self_reports: OSHA injury logs, EPA TRI releases). All figures are peer aggregates — no individual peer is named. Returns 422 NO_INDUSTRY_CODE if the entity has neither a NAICS nor SIC code.

Request
GET /v1/entity/ent_321d75/benchmark
X-API-Key: pt_live_xxx
Response
{
  "entity_id": "ent_321d75",
  "name": "Acme Pharmaceuticals Inc",
  "industry": { "naics": "325412", "sic": "2834", "entity_count": 142 },
  "violations": {
    "entity_count": 29,
    "industry_median": 2.0,
    "industry_average": 6.0,
    "reporter_count": 38,
    "percentile": 94.0,
    "rating": "well_above_average"
  },
  "penalties": {
    "entity_total_usd": 890000,
    "industry_median_usd": 13789.5,
    "industry_average_usd": 63373.22,
    "reporter_count": 21,
    "percentile": 97.0,
    "rating": "well_above_average"
  },
  "complaints": {
    "cfpb_count": 0,
    "industry_median": 0.0,
    "reporter_count": 0,
    "percentile": 0.0
  },
  "self_reports": {
    "osha_injury_count": 12,
    "osha_industry_median": 223.0,
    "osha_reporter_count": 31,
    "osha_percentile": 18.0,
    "epa_release_count": 0,
    "epa_industry_median": 0.0,
    "epa_reporter_count": 0,
    "epa_percentile": 0.0
  }
}
GET /v1/entity/{entity_id}/corporate-tree

Map parent/subsidiary relationships, drawing primarily on the entity_relationships graph (SEC Exhibit 21, FDIC, Fed NIC, GLEIF, SEC 13D/G), with EPA TRI parent fields, DUNS numbers, and name matching as fallback. SQL-based — no AI involved. Costs 1 credit.

Request
GET /v1/entity/ent_321d75/corporate-tree
X-API-Key: pt_live_xxx
Response
{
  "data": {
    "entity_id": "ent_e36d8ec996d299a5",
    "name": "AMAZON COM INC",
    "parent": null,
    // parent is {entity_id, name, source} or null
    "subsidiaries": [
      { "entity_id": "ent_a9e1c60167e16370", "name": "AMAZON TECHNOLOGIES, INC.", "relationship_source": "sec_exhibit21", "confidence": 1.0 },
      { "entity_id": "ent_aa3b74350176b73f", "name": "Amazon EU S.à r.l.", "relationship_source": "gleif", "confidence": 1.0 }
      // relationship_source: sec_exhibit21, fdic, fed_nic, gleif, sec_13dg
    ],
    "related_entities": []
    // items: {entity_id, name, relationship, source}
  },
  "meta": { "request_id": "req_d72061fe60de", "credits_used": 1, "credits_remaining": 19934 },
  "pagination": null
}
GET /v1/entity/{entity_id}/relationships

Every corporate-family edge we hold for an entity — parents, subsidiaries, ultimate parents, beneficial owners, branches, funds — with per-edge source, status, and effective dates. Costs 1 credit.

Parameters

type string Optional label filter (e.g. subsidiary, parent, beneficial_owner). Returns all types if omitted.
page integer Page number. Default 1.
per_page integer Results per page. Default 50, max 100.
Request
GET /v1/entity/ent_321d75/relationships?page=1&per_page=50
X-API-Key: pt_live_xxx
Response
{
  "data": [{
    "relationship": "subsidiary",
    "direction": "child",
    "counterparty": {"entity_id": "ent_1a2b3c", "name": "EXAMPLE SUB LLC"},
    "type_raw": "subsidiary_of",
    "source": "sec_exhibit21",
    "status": "active",
    "effective_from": "2019-12-31",
    "effective_to": null,
    "last_seen_at": "2026-06-30T00:00:00Z"
  }],
  "type_counts": {"subsidiary": 180, "parent": 1, "beneficial_owner": 6},
  "pagination": {"page": 1, "per_page": 50, "total": 187, "has_more": true}
}
GET /v1/persons/{person_id}

Full person profile: career timeline, name variants, identifiers, and same_name_leads — other person records sharing the identical name, surfaced as explicitly unverified pivots (never merged automatically). See the identity note above; check resolution before treating the record as a verified identity. Costs 1 credit.

Response
{
  "data": {
    "person_id": "per_9f8e7d6c",
    "canonical_name": "jane q example",
    "resolution": "anchored",
    "identifiers": [{"type": "cik", "value": "0001234567"}],
    "roles": [{"entity_id": "ent_1a2b3c", "role_type": "director", "year_start": 2024}],
    "same_name_leads": [{"person_id": "per_5b4a3f2e", "entity_name": "OTHER CO INC", "role_types": ["beneficial_owner"]}]
  }
}
GET /v1/entity/{entity_id}/roster

Executives, directors, and individual beneficial owners at one company, as reported in its SEC filings (proxy statements, 13D/G cover pages). Optional year parameter restricts to one filing year. Names are as-filed and name-resolved within this company only — see the identity note above. Costs 1 credit.

Request
GET /v1/entity/ent_321d75/roster?year=2026
X-API-Key: pt_live_xxx

Rate Limits

Rate limits are applied per API key. When you exceed your rate limit, requests return a 429 status code. The response includes Retry-After and X-RateLimit-* headers.

Plan Rate Limit Monthly Credits
Free 1 request/sec 200/month
Pro 5 requests/sec 2,000/month
Team 10 requests/sec 10,000/month
Enterprise Custom Custom
Rate Limit Headers
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1710504000
X-Credits-Remaining: 24847

Error Codes

All errors return a consistent JSON structure with a machine-readable code and a human-readable message.

Error Response Format
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 1 second.",
    "status": 429
  }
}
Status Code Description
400 bad_request Invalid request parameters. Check your query string or request body.
401 unauthorized Missing or invalid API key. Ensure your X-API-Key header is set correctly.
403 forbidden Your plan does not include access to this endpoint or feature.
404 not_found The requested entity or resource does not exist.
429 rate_limit_exceeded Too many requests per second. Check the Retry-After header.
429 credits_exhausted Monthly API credit quota reached. Upgrade your plan or wait for renewal.
500 internal_error Unexpected server error. If persistent, contact support.
503 AI_GENERATION_FAILED AI model returned an error. Retry after 30 seconds. Cached AI responses don't consume credits.

Glossary

Quick reference for the identifiers, filing types, and source authorities that show up across the API. Skim the column that matches what you're working with.

Identifiers

CIK
Central Index Key. SEC's per-entity ID for any filer in EDGAR. 10-digit zero-padded.
EIN
Employer Identification Number. IRS-issued tax ID. XX-XXXXXXX.
LEI
Legal Entity Identifier. ISO 17442 / GLEIF-issued global ID for entities engaging in financial transactions. 20 characters.
DUNS
Data Universal Numbering System. Dun & Bradstreet 9-digit ID. Replaced by UEI in federal contracting (April 2022) but persists in legacy datasets.
UEI
Unique Entity Identifier. SAM.gov's 12-char alphanumeric ID. Per-registration, not per-entity — one company can hold many UEIs.
NPI
National Provider Identifier. CMS-issued 10-digit ID for healthcare providers (organisations and individuals).
CRD / IARD
FINRA's Central Registration Depository number (broker-dealers) and SEC's Investment Adviser Registration Depository number (RIAs).
CAGE
Commercial and Government Entity Code. DLA-issued 5-char ID for federal-contractor sites.
FDIC Cert
FDIC Certificate Number. Unique to each insured bank.
RSSD
Federal Reserve's primary entity ID for banking institutions tracked in the National Information Center.
OCC Charter
Office of the Comptroller of the Currency charter number. National-bank ID.
SIC / NAICS
Industry classification codes. SIC is the 4-digit legacy system; NAICS is the 6-digit modern replacement.

Filings & Forms

10-K
Annual report. Audited financials, business overview, risk factors, MD&A. Required for US public companies.
10-Q
Quarterly report. Unaudited financials. Three filings per fiscal year (the fourth quarter is rolled into the 10-K).
8-K
Current report. Material events disclosed within 4 business days — M&A, executive departures, bankruptcy, regulation FD events.
DEF 14A
Definitive proxy statement. Annual-meeting agenda + executive compensation + director nominees.
Form 4
Insider transaction report. Filed within 2 business days of a Section 16 insider's trade.
Form D
Notice of Sale of Securities. Used for Reg-D private placements (start-up funding rounds, hedge-fund offerings).
Schedule 13D / 13G
Beneficial-ownership reports. Triggered when a shareholder crosses 5%. 13D is active (intent to influence); 13G is passive.
Form 990
Annual return for tax-exempt orgs. Required for 501(c)(3) and most non-profits with gross receipts > $200K.
Exhibit 21
List of subsidiaries. Filed as an exhibit to the 10-K. Source of corporate-tree edges.
S-1 / S-4
Registration statements. S-1 for IPOs and new offerings; S-4 for M&A consideration shares.
Form 300A
OSHA injury & illness annual summary. Establishment-level reporting for high-hazard industries via the ITA portal.

Sources & Concepts

EDGAR
SEC's Electronic Data Gathering, Analysis, and Retrieval system. Public-company filings since 1993.
OFAC SDN
Office of Foreign Assets Control's Specially Designated Nationals list. US economic-sanctions blocklist.
OSHA ITA
Occupational Safety and Health Administration's Injury Tracking Application. Form 300A summaries for covered employers.
EPA TRI
Toxic Release Inventory. Annual chemical-release reporting under EPCRA Section 313.
SAM.gov
System for Award Management. US federal-contractor registration database, plus the federal exclusions list.
OpenSanctions
Aggregated sanctions / PEP / watchlist feed combining 100+ upstream lists (OFAC, EU FSF, UN SC, UK FCDO, …).
GLEIF
Global Legal Entity Identifier Foundation. Issues LEIs and publishes the daily GLEIF reference file.
Entity resolution
Collapsing name variants ("ACME CORP", "Acme Inc.") into a single canonical record using identifier matches, name similarity, and corporate-tree edges.
Source record
One row from one upstream feed — one filing, one violation, one sanction listing. Many source records resolve to one entity.
Canonical name
The chosen primary name for an entity. Other observed forms live as aliases.
Strong identifier
An ID guaranteed unique per entity (CIK, EIN, LEI, FDIC Cert, RSSD, IARD, OCC Charter). Used to merge with high confidence.
Weak identifier
An attribute that isn't strictly per-entity (UEI is per-registration, names & addresses are fuzzy). Needs supporting evidence to drive a merge.