API documentation
Programmatic access to the Two Minute Warning threat-intel substrate. JSON REST API, paginated, idiomatic, deduplicated.
Quick start
One curl call to look up a single CVE:
curl https://f1tym1.com/wp-json/f1tym1/v1/entity/cve/CVE-2024-3094 \
-H "Authorization: Bearer YOUR_API_KEY"
That returns a single JSON object with the CVE's metadata, CVSS scores, connected weaknesses, and source provenance. The full response shape is described in Response shape.
Authentication
Every API call requires an API key sent as a Bearer token in the Authorization header. Keys are tied to your account and your subscription tier, which determines your rate limits.
Get a key
Keys are issued on signup. After completing checkout (or free-tier sign-up), you'll receive an email with a sign-in link to your dashboard. On the dashboard's first load, your API key is displayed once. Copy it then - only a bcrypt hash of the key is kept on our side, so we can't show it again.
Keys have the format tmw_live_<48 hex chars> for live mode (or tmw_test_<48 hex chars> for sandbox keys, if you've been issued one for development).
Use a key
Standard Bearer header pattern:
Authorization: Bearer tmw_live_abc123def456...
As a fallback for browser-side testing, the ?api_key= query parameter is also accepted:
https://f1tym1.com/wp-json/f1tym1/v1/entity/cve/CVE-2024-3094?api_key=tmw_live_abc...
The Bearer header is preferred for production traffic - keys in query parameters often end up in server logs, browser history, and analytics tools. Use the header for anything sensitive.
Rotate a key
Rotate from the dashboard at any time. Rotation:
- Invalidates the existing key immediately (subsequent requests with the old key return
401 rest_api_invalid_key). - Issues a new key, shown once, in the dashboard.
- Resets your "last used" timestamp.
Usage counters and subscription state are preserved across rotation - you don't lose your monthly cap.
Rate limits
Limits are tier-based. Caps are enforced per API key, on rolling daily or monthly windows in UTC.
| Tier | Cap | Period | Overage |
|---|---|---|---|
| Free | 200 requests | day (UTC) | Hard 429 at cap; no overage |
| Pro | 100,000 requests | month (UTC) | $0.01 / request past cap, billed monthly |
| Team | 300,000 requests | month (UTC) | $0.01 / request past cap, billed monthly |
| Business | 1,000,000 requests | month (UTC) | $0.01 / request past cap, billed monthly |
Response headers
Every authenticated response includes:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your tier's request cap for the current period |
X-RateLimit-Used | Requests counted in the current period (including this one) |
X-RateLimit-Remaining | max(0, Limit − Used) |
X-RateLimit-Reset | Unix epoch second when the current period rolls over |
X-RateLimit-Period | day or month |
When you hit your cap
Free tier: the next request returns 429 rest_api_rate_limited immediately, with the same rate-limit headers showing Remaining: 0 and the next reset time. Wait until X-RateLimit-Reset, or upgrade your tier.
Paid tiers: requests past your cap continue to return 200. The X-RateLimit-Used header will exceed X-RateLimit-Limit, and overage is added to your next monthly invoice at $0.01 per request. There is no service interruption.
Endpoints
All endpoints live under base URL:
https://f1tym1.com/wp-json/f1tym1/v1/entity
Every entity type supports the same two routes:
/entity/{type}/{id} - single record/entity/{type} - paginated list / browseCommon query parameters (list endpoints)
| Param | Type | Default | Notes |
|---|---|---|---|
page | integer | 1 | Page number, 1-indexed |
per_page | integer | 25 | Max 100 per page |
order | string | tier_score | Sort field; default ranks by tier score descending |
min_score | integer | 0 | Only return records with tier score ≥ this value |
tier | string | - | b = Tier B-eligible only (active in the last 90 days) |
Type-specific filters
| Param | Applies to | Notes |
|---|---|---|
kev_only | cve | Only CVEs listed in CISA KEV |
in_the_wild | cve | Only CVEs with confirmed in-the-wild exploitation |
active | actor, campaign | Active in last 90 days only |
Response shape
Every entity uses a uniform envelope. The entity-specific payload lives in data. Other fields are stable across all 8 entity types.
{
"type": "cve",
"id": "CVE-2024-3094",
"name": "CVE-2024-3094",
"url": "https://f1tym1.com/cve/CVE-2024-3094/",
"schema_org_type": "Vulnerability",
"data": { /* entity-specific fields - see each type below */ },
"tier": {
"score": 0,
"b_eligible": false,
"data_quality_score": 85,
"last_verified_at": "2026-06-02T17:48:57+00:00",
"is_public": true
},
"external_refs": { "nvd_source": "nist" },
"tags": null,
"notes": null,
"corpus_mention_count": 0,
"created_at": "2026-06-01T18:31:17+00:00",
"updated_at": "2026-06-02T13:48:57+00:00",
"connected": {
"exploits": [],
"has_weakness": [
{
"type": "weakness",
"id": "CWE-506",
"name": "Embedded Malicious Code",
"url": "https://f1tym1.com/weakness/CWE-506/",
"confidence": 95,
"source_evidence": "nvd",
"disputed": false
}
]
},
"coverage": [],
"meta": {
"version": "0.3",
"generated_at": "2026-06-03T02:58:02+00:00"
}
}
List endpoints wrap the items in an envelope with pagination:
{
"items": [ /* array of entity objects */ ],
"page": 1,
"per_page": 25,
"total": 354922,
"meta": { "version": "0.3", "generated_at": "..." }
}
Envelope fields
| Field | Notes |
|---|---|
type | One of: cve, actor, campaign, malware, technique, weakness, attack_pattern, mitigation |
id | Canonical identifier (CVE ID, MITRE ID, CWE ID, CAPEC ID, etc.) |
name | Human-readable name |
url | Canonical web page URL (for browser viewing) |
schema_org_type | Matching schema.org type for structured-data interop |
data | Entity-type-specific payload |
tier | Editorial tier score + quality metadata. b_eligible = active in last 90 days |
external_refs | Source provenance (NVD, MITRE, CISA, etc.) |
connected | (single-record only) Relationships to other entities |
coverage | (single-record only) Published articles mentioning this entity |
meta.version | API contract version - bump means breaking change |
CVE
Vulnerabilities, sourced from NVD with CISA KEV cross-references and corpus-derived exploitation signals.
/entity/cve/{cve_id}/entity/cvedata fields
| Field | Type | Notes |
|---|---|---|
cve_id | string | e.g. CVE-2024-3094 |
summary | string | NVD description (English) |
cvss_v3_score | float | 0.0-10.0 |
cvss_v3_vector | string | e.g. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H |
cvss_v4_score | float? | Null if NVD hasn't published v4 yet |
cvss_v4_vector | string? | Full v4 vector string |
cwe | string? | CWE identifier, e.g. CWE-79 |
published_date | ISO 8601 | NVD publish timestamp |
last_modified | ISO 8601 | NVD last-modified timestamp |
kev_listed | bool | Listed in CISA KEV catalog |
kev_date | ISO 8601? | Date added to KEV (null if not listed) |
in_the_wild | bool | Confirmed exploitation observed |
poc_known | bool | Public PoC published |
poc_first_seen | ISO 8601? | First PoC observation |
affected_products | array | List of {vendor, product, version_range} |
vendor_patch_info | object? | {url, tags} for the primary patch reference |
Example
$ curl https://f1tym1.com/wp-json/f1tym1/v1/entity/cve/CVE-2024-3094 \
-H "Authorization: Bearer $TMW_KEY"
{
"type": "cve",
"id": "CVE-2024-3094",
"name": "CVE-2024-3094",
"url": "https://f1tym1.com/cve/CVE-2024-3094/",
"schema_org_type": "Vulnerability",
"data": {
"cve_id": "CVE-2024-3094",
"summary": "Malicious code was discovered in the upstream tarballs of xz...",
"cvss_v3_score": 10,
"cvss_v3_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"cwe": "CWE-506",
"published_date": "2024-03-29T17:15:21+00:00",
"kev_listed": false,
"affected_products": [{ "vendor": "tukaani", "product": "xz", "version_range": "" }]
},
"connected": {
"has_weakness": [
{ "type": "weakness", "id": "CWE-506", "name": "Embedded Malicious Code", "confidence": 95 }
]
}
}
Actor
Threat actors / intrusion sets, aligned to MITRE ATT&CK.
/entity/actor/{slug}/entity/actordata fields
| Field | Type | Notes |
|---|---|---|
slug | string | Canonical slug, e.g. wizard-spider |
name | string | Primary display name |
aliases | array<string> | Known aliases (e.g. UNC1878, TEMP.MixMaster) |
mitre_id | string? | MITRE ATT&CK Group ID (e.g. G0102) |
description | string | MITRE-sourced summary |
first_seen | ISO 8601? | Approximate first observation |
last_seen | ISO 8601? | Most recent corpus mention |
Campaign
Named threat campaigns (MITRE ATT&CK campaign objects).
/entity/campaign/{slug}/entity/campaignMalware
Malware families / tools (MITRE ATT&CK malware and tool objects).
/entity/malware/{slug}/entity/malwareTechnique
MITRE ATT&CK techniques and sub-techniques (T-numbers).
/entity/technique/{technique_id}/entity/techniqueWeakness
CWE weaknesses (Common Weakness Enumeration).
/entity/weakness/{cwe_id}/entity/weaknessAttack pattern
CAPEC attack patterns.
/entity/attack_pattern/{capec_id}/entity/attack_patternMitigation
MITRE ATT&CK mitigations (M-numbers).
/entity/mitigation/{mitigation_id}/entity/mitigationErrors
Errors are returned as JSON objects with a stable code string, a human-readable message, and the matching HTTP status. Codes are guaranteed stable across non-major versions - log them in monitoring.
Common envelope
{
"code": "rest_api_invalid_key",
"message": "API key invalid or revoked.",
"data": { "status": 401 }
}
Reference
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_email | Email field missing or malformed (signin / signup) |
| 400 | unknown_entity_type | Path contains an entity type not in the supported list |
| 400 | no_subscription | Billing portal requested but no Stripe subscription on file |
| 401 | rest_api_unauthorized | Missing or empty Authorization header / ?api_key param |
| 401 | rest_api_invalid_key | API key bcrypt-check failed or key has been rotated |
| 401 | invalid_token | Magic-link or session token expired, invalid, or signature mismatch |
| 401 | unauthorized | Account / dashboard endpoint hit without a valid session token |
| 403 | rest_api_no_tier | Auth succeeded but no tier is configured for the user (rare - contact support) |
| 404 | rest_no_route | URL path does not match any registered endpoint |
| 409 | email_in_use | Signup attempted with an email already registered |
| 429 | rest_api_rate_limited | Free tier exceeded its daily cap |
| 503 | billing_disabled | Sign-up + checkout are temporarily disabled by the operator |
Versioning
The contract version is exposed on every response under meta.version. Current version: 0.3.
What counts as a breaking change
- Removing or renaming an existing field
- Changing a field's type (e.g., string → object)
- Adding a required query parameter
- Changing the meaning of an existing error
code
What does NOT count as a breaking change
- Adding new fields to a response (clients must ignore unknown fields)
- Adding new entity types under
/entity/{new_type} - Adding new error codes for previously-unhandled conditions
- Adding new query parameters with sensible defaults
- Changing rate-limit caps (announced in advance via the dashboard)
Breaking changes ship with a major version bump (e.g. 0.3 → 1.0). The old contract is supported in parallel for at least 90 days, with a deprecation header (Deprecation: true) on the legacy endpoint.
Questions or issues? Sign in to your dashboard.