{
 "openapi": "3.1.0",
 "info": {
  "title": "FirmTape Geo API",
  "version": "1.0.0",
  "summary": "Dated public records of world events, each with its source and time.",
  "description": "Government notices and sanctions, flight restrictions, maritime warnings, internet outages, conflict and disaster records, each against its own normal. The free JSON needs no key and covers the last 30 days. The paid API (API and Team plans, https://firmtape.com/geo/pricing) takes a key, covers a year and returns every field. Guide with examples, webhooks and MCP: https://firmtape.com/geo/docs. Records are context, never a trading signal; credit each record's source and link.",
  "contact": {
   "name": "FirmTape",
   "url": "https://firmtape.com/geo/docs",
   "email": "support@firmtape.com"
  },
  "termsOfService": "https://firmtape.com/terms"
 },
 "externalDocs": {
  "description": "Geo API docs",
  "url": "https://firmtape.com/geo/docs"
 },
 "servers": [
  {
   "url": "https://firmtape.com/geo/api",
   "description": "Free, no key: the last 30 days, as the Geo pages show them"
  },
  {
   "url": "https://geo.firmtape.com",
   "description": "Paid Geo API: API or Team plan key, a year of history"
  }
 ],
 "tags": [
  {
   "name": "Free",
   "description": "No key, the last 30 days"
  },
  {
   "name": "Paid",
   "description": "Authorization: Bearer gk_..., API or Team plan"
  }
 ],
 "paths": {
  "/items": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeItems",
    "summary": "Records, free",
    "parameters": [
     {
      "name": "week",
      "in": "query",
      "required": false,
      "description": "ISO week on the New York calendar, e.g. 2026-W38; replaces range",
      "schema": {
       "type": "string",
       "pattern": "^\\d{4}-W\\d{2}$"
      }
     },
     {
      "name": "range",
      "in": "query",
      "required": false,
      "description": "24h, 7d or 30d when no week is given",
      "schema": {
       "type": "string",
       "enum": [
        "24h",
        "7d",
        "30d"
       ],
       "default": "24h"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "notable",
      "in": "query",
      "required": false,
      "description": "1 keeps only notable records",
      "schema": {
       "type": "string",
       "enum": [
        "1"
       ]
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Up to 1000",
      "schema": {
       "type": "integer",
       "maximum": 1000,
       "default": 200
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "Paging inside a week",
      "schema": {
       "type": "integer",
       "minimum": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Records, newest first, with week paging links",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Record"
           }
          },
          "next_page": {
           "type": [
            "string",
            "null"
           ]
          },
          "prev_week": {
           "type": [
            "string",
            "null"
           ]
          },
          "next_week": {
           "type": [
            "string",
            "null"
           ]
          }
         }
        }
       }
      }
     }
    },
    "x-slug": "free-items",
    "description": "Records of one week or a recent range, as the pages show them.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days"
   }
  },
  "/weeks": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeWeeks",
    "summary": "Weeks with records, free",
    "parameters": [
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Weeks, newest first"
     }
    },
    "x-slug": "free-weeks",
    "description": "Weeks of the last 30 days that hold records, with counts.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days"
   }
  },
  "/countries": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeCountries",
    "summary": "Countries named, free",
    "responses": {
     "200": {
      "description": "Countries"
     }
    },
    "x-slug": "free-countries",
    "description": "Every country named in the last 30 days, with notable and total counts.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days"
   }
  },
  "/health": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeSources",
    "summary": "Feeds and their health, free",
    "responses": {
     "200": {
      "description": "Sources"
     }
    },
    "x-slug": "free-sources",
    "description": "Every feed, how often it is read and when it last answered.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days"
   }
  },
  "/v1/items": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "items",
    "summary": "Records",
    "security": [
     {
      "key": []
     }
    ],
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC start; default 7 days ago",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "until",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC end; default now",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "feed",
      "in": "query",
      "required": false,
      "description": "One feed id or several, comma separated",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_severity",
      "in": "query",
      "required": false,
      "description": "2 notable and above, 3 critical only",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "maximum": 3
      }
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "description": "Words in the title",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "uid",
      "in": "query",
      "required": false,
      "description": "One record id, or several comma separated; ignores the time window",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1 to 1000",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 1000,
       "default": 200
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "From next in the previous answer",
      "schema": {
       "type": "integer",
       "minimum": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Records; next holds the URL of the next page when more match",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "next": {
           "type": [
            "string",
            "null"
           ]
          },
          "history_days": {
           "type": "integer"
          },
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Record"
           }
          }
         }
        },
        "example": {
         "count": 1,
         "next": null,
         "history_days": 365,
         "items": [
          {
           "uid": "8f1c2a90d4b7e613",
           "feed": "faa_tfr",
           "sector": "air",
           "type": "new",
           "category": "TFR",
           "severity": 2,
           "routine": false,
           "countries": [
            "US"
           ],
           "region": null,
           "title": "Temporary flight restriction",
           "summary": "...",
           "published_utc": "2026-09-17T12:58:00Z",
           "event_utc": null,
           "first_seen_utc": "2026-09-17T13:02:00Z",
           "backfill": false,
           "source": "FAA",
           "url": "https://example.org/notice"
          }
         ]
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong; no call is counted",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "No key, unknown key or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "No active API or Team plan, or a key beyond the plan's count",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "This month's calls are used; Retry-After says when the count resets",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "x-slug": "items",
    "description": "Records, newest first, a year back.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  },
  "/v1/sources": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "sources",
    "summary": "Feeds",
    "security": [
     {
      "key": []
     }
    ],
    "responses": {
     "200": {
      "description": "One row per feed: feed, source, sector, last_ok_utc, minutes_since_ok, last_status, fail_streak, records, first_record_utc, last_record_utc"
     },
     "401": {
      "description": "Bad key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "x-slug": "sources",
    "description": "Every feed, its sector, when it last answered and what it holds.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  },
  "/v1/usage": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "usage",
    "summary": "This month's usage",
    "security": [
     {
      "key": []
     }
    ],
    "responses": {
     "200": {
      "description": "Usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "tier": {
           "type": "string"
          },
          "month": {
           "type": "string"
          },
          "calls": {
           "type": "integer"
          },
          "limit": {
           "type": "integer"
          }
         }
        }
       }
      }
     }
    },
    "x-slug": "usage",
    "description": "This month's calls against the plan; not counted.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "Not counted"
   }
  },
  "/v1/export.csv": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "exportCsv",
    "summary": "CSV export",
    "security": [
     {
      "key": []
     }
    ],
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC start",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "until",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC end",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "feed",
      "in": "query",
      "required": false,
      "description": "Feed ids",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_severity",
      "in": "query",
      "required": false,
      "description": "Severity floor",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "CSV",
      "content": {
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "400": {
      "description": "Window longer than the plan allows",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "x-slug": "export-csv",
    "description": "Every matching record in one CSV: up to 7 days a call on API, 31 on Team.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  }
 },
 "components": {
  "securitySchemes": {
   "key": {
    "type": "http",
    "scheme": "bearer",
    "description": "A Geo API key (gk_...) from https://firmtape.com/account#geo-keys. The same key opens the MCP server at https://geo.firmtape.com/mcp."
   }
  },
  "schemas": {
   "Record": {
    "type": "object",
    "properties": {
     "uid": {
      "type": "string",
      "description": "Stable id of the record"
     },
     "feed": {
      "type": "string",
      "description": "Feed id, see /v1/sources or https://firmtape.com/geo/sources"
     },
     "sector": {
      "type": "string",
      "enum": [
       "gov",
       "air",
       "gps",
       "sea",
       "internet",
       "ground",
       "space",
       "attention"
      ]
     },
     "type": {
      "type": "string",
      "description": "new, update, level_raise, level_lower, cancel, spike, keyword or no_data"
     },
     "category": {
      "type": "string"
     },
     "severity": {
      "type": "integer",
      "description": "0 info, 1 routine, 2 notable, 3 critical"
     },
     "routine": {
      "type": "boolean",
      "description": "True for the records the pages hide by default"
     },
     "countries": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "ISO 3166-1 alpha-2"
     },
     "region": {
      "type": [
       "string",
       "null"
      ]
     },
     "title": {
      "type": "string"
     },
     "summary": {
      "type": "string"
     },
     "published_utc": {
      "type": "string",
      "format": "date-time",
      "description": "When the source published it"
     },
     "event_utc": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time",
      "description": "When the activity happens or starts, when the record says"
     },
     "first_seen_utc": {
      "type": "string",
      "format": "date-time",
      "description": "When FirmTape first read it"
     },
     "backfill": {
      "type": "boolean",
      "description": "Found in the source's own history rather than caught live"
     },
     "source": {
      "type": "string",
      "description": "The publisher to credit"
     },
     "url": {
      "type": "string",
      "description": "The record at its source"
     },
     "attribution": {
      "type": [
       "string",
       "null"
      ],
      "description": "Paid API only: the citation the source asks for"
     },
     "value": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: what was measured"
     },
     "baseline": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: its own normal"
     },
     "score": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: value against the normal"
     },
     "lat": {
      "type": [
       "number",
       "null"
      ],
      "description": "Paid API only"
     },
     "lon": {
      "type": [
       "number",
       "null"
      ],
      "description": "Paid API only"
     },
     "facts": {
      "type": "object",
      "description": "Paid API only: source-specific details"
     }
    }
   },
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "string"
     }
    }
   }
  }
 },
 "x-product": "geo",
 "x-features": [
  "geo-api",
  "geo-alerts"
 ]
}