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.

Don't have a key yet? Visit the pricing page, pick a tier, and you'll receive your API key in your dashboard on first sign-in.

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.

TierCapPeriodOverage
Free200 requestsday (UTC)Hard 429 at cap; no overage
Pro100,000 requestsmonth (UTC)$0.01 / request past cap, billed monthly
Team300,000 requestsmonth (UTC)$0.01 / request past cap, billed monthly
Business1,000,000 requestsmonth (UTC)$0.01 / request past cap, billed monthly

Response headers

Every authenticated response includes:

HeaderMeaning
X-RateLimit-LimitYour tier's request cap for the current period
X-RateLimit-UsedRequests counted in the current period (including this one)
X-RateLimit-Remainingmax(0, Limit − Used)
X-RateLimit-ResetUnix epoch second when the current period rolls over
X-RateLimit-Periodday 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:

GET /entity/{type}/{id} - single record
GET /entity/{type} - paginated list / browse

Common query parameters (list endpoints)

ParamTypeDefaultNotes
pageinteger1Page number, 1-indexed
per_pageinteger25Max 100 per page
orderstringtier_scoreSort field; default ranks by tier score descending
min_scoreinteger0Only return records with tier score ≥ this value
tierstring-b = Tier B-eligible only (active in the last 90 days)

Type-specific filters

ParamApplies toNotes
kev_onlycveOnly CVEs listed in CISA KEV
in_the_wildcveOnly CVEs with confirmed in-the-wild exploitation
activeactor, campaignActive 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

FieldNotes
typeOne of: cve, actor, campaign, malware, technique, weakness, attack_pattern, mitigation
idCanonical identifier (CVE ID, MITRE ID, CWE ID, CAPEC ID, etc.)
nameHuman-readable name
urlCanonical web page URL (for browser viewing)
schema_org_typeMatching schema.org type for structured-data interop
dataEntity-type-specific payload
tierEditorial tier score + quality metadata. b_eligible = active in last 90 days
external_refsSource provenance (NVD, MITRE, CISA, etc.)
connected(single-record only) Relationships to other entities
coverage(single-record only) Published articles mentioning this entity
meta.versionAPI contract version - bump means breaking change

CVE

Vulnerabilities, sourced from NVD with CISA KEV cross-references and corpus-derived exploitation signals.

GET /entity/cve/{cve_id}
GET /entity/cve

data fields

FieldTypeNotes
cve_idstringe.g. CVE-2024-3094
summarystringNVD description (English)
cvss_v3_scorefloat0.0-10.0
cvss_v3_vectorstringe.g. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
cvss_v4_scorefloat?Null if NVD hasn't published v4 yet
cvss_v4_vectorstring?Full v4 vector string
cwestring?CWE identifier, e.g. CWE-79
published_dateISO 8601NVD publish timestamp
last_modifiedISO 8601NVD last-modified timestamp
kev_listedboolListed in CISA KEV catalog
kev_dateISO 8601?Date added to KEV (null if not listed)
in_the_wildboolConfirmed exploitation observed
poc_knownboolPublic PoC published
poc_first_seenISO 8601?First PoC observation
affected_productsarrayList of {vendor, product, version_range}
vendor_patch_infoobject?{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.

GET /entity/actor/{slug}
GET /entity/actor

data fields

FieldTypeNotes
slugstringCanonical slug, e.g. wizard-spider
namestringPrimary display name
aliasesarray<string>Known aliases (e.g. UNC1878, TEMP.MixMaster)
mitre_idstring?MITRE ATT&CK Group ID (e.g. G0102)
descriptionstringMITRE-sourced summary
first_seenISO 8601?Approximate first observation
last_seenISO 8601?Most recent corpus mention

Campaign

Named threat campaigns (MITRE ATT&CK campaign objects).

GET /entity/campaign/{slug}
GET /entity/campaign

Malware

Malware families / tools (MITRE ATT&CK malware and tool objects).

GET /entity/malware/{slug}
GET /entity/malware

Technique

MITRE ATT&CK techniques and sub-techniques (T-numbers).

GET /entity/technique/{technique_id}
GET /entity/technique

Weakness

CWE weaknesses (Common Weakness Enumeration).

GET /entity/weakness/{cwe_id}
GET /entity/weakness

Attack pattern

CAPEC attack patterns.

GET /entity/attack_pattern/{capec_id}
GET /entity/attack_pattern

Mitigation

MITRE ATT&CK mitigations (M-numbers).

GET /entity/mitigation/{mitigation_id}
GET /entity/mitigation

Errors

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

StatusCodeMeaning
400invalid_emailEmail field missing or malformed (signin / signup)
400unknown_entity_typePath contains an entity type not in the supported list
400no_subscriptionBilling portal requested but no Stripe subscription on file
401rest_api_unauthorizedMissing or empty Authorization header / ?api_key param
401rest_api_invalid_keyAPI key bcrypt-check failed or key has been rotated
401invalid_tokenMagic-link or session token expired, invalid, or signature mismatch
401unauthorizedAccount / dashboard endpoint hit without a valid session token
403rest_api_no_tierAuth succeeded but no tier is configured for the user (rare - contact support)
404rest_no_routeURL path does not match any registered endpoint
409email_in_useSignup attempted with an email already registered
429rest_api_rate_limitedFree tier exceeded its daily cap
503billing_disabledSign-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.