Developer Documentation

Build on GlobalGov

Connect AI assistants and back-end systems to 9.9M+ government procurement records — solicitations, tenders, RFPs and grants. National procurement sources in 60 countries and records from 193 in total, updated daily. Start with the live MCP server or the REST API.

Getting started

Who can access the API

The MCP server has a genuinely free tier — https://freemcp.globalgov.io/mcp, no key, no sign-in (see Connect an assistant). Full programmatic access (the AI brief, live feed, full-depth pagination, all regions, and the REST + Developer API) is a paid add-on:

  • The API & MCP add-on is $400/mo, available only on the Global and Enterprise+ plans.
  • It is not included in any plan. Even Global and Enterprise+ purchase it separately as an add-on; it is not bundled and it is not free.
  • Scout, Essentials, Intelligence are not eligible. Upgrade to Global or Enterprise+ to add it.

All 5 MCP tools work with no key at all at teaser depth (platform stats, search, factual record lookups, related opportunities) — see Free tier vs. paid tier.

Get an API key

  1. Sign in and open Settings → API & MCP Access (/settings/api).
  2. Click Buy API Access to add the add-on. No plan includes it, so this step applies to every account.
  3. Click Generate API key. Your key (format gg_…) is shown once — copy it immediately. It is stored hashed and can never be shown again; lose it and you regenerate.

Keys can be regenerated (rotated) or revoked at any time from the same page. Revoking a key breaks any assistant or integration still using it.

Base URLs & authentication

SurfaceBase URLAuth
MCP server (free — start here)https://freemcp.globalgov.io/mcpNone — no sign-in, 5 tools at teaser depth
MCP server (full access)https://mcp.globalgov.ioOAuth ("Sign in with GlobalGov") or X-API-Key
MCP (keyless twin of full host)https://mcp.globalgov.io/publicNone — anonymous teaser
REST APIhttps://globalgov.io/api/v1X-API-Key header

AI assistants authenticate over OAuth ("Sign in with GlobalGov"). For direct REST / BYO callers, authenticate every request with your key in the X-API-Key header:

http
X-API-Key: gg_your_key_here

All access is over HTTPS. Never embed a key in client-side/browser code or a public repository — treat it like a password.

Live · recommended

MCP connector guide

The Model Context Protocol (MCP) is the open standard AI assistants use to connect to external tools and data. GlobalGov runs a remote MCP server so an assistant can search and read procurement intelligence on your behalf — no glue code required.

Endpointhttps://mcp.globalgov.io
TransportStreamable HTTP (stateless JSON), JSON-RPC 2.0
Protocol2025-06-18 (also negotiates 2025-03-26, 2024-11-05)
Methodsinitialize, ping, tools/list, tools/call
AuthOAuth 2.1 ("Sign in with GlobalGov") or an X-API-Key header. See Connect an assistant.

Start free. https://freemcp.globalgov.io/mcp needs no sign-in and no key — add it to any MCP client and all 5 tools appear at teaser depth. For the AI brief on each record, the live opportunity feed, full-depth pagination, and all your subscribed regions, use https://mcp.globalgov.io and sign in with GlobalGov (or send an X-API-Key header). https://mcp.globalgov.io/public is the same free teaser on the paid host. All are POST-only; GET/DELETE return 405, and JSON-RPC batching is not supported.

Connect an assistant

Start with the free URL https://freemcp.globalgov.io/mcp — no account, no key, no sign-in. It works on every client below that supports remote MCP. To unlock full depth (AI briefs, live feed, full pagination, all regions), swap in https://mcp.globalgov.io and either "Sign in with GlobalGov" (OAuth, zero-config via Dynamic Client Registration) or send an X-API-Key header. Verified against each vendor's current docs (2026).

Free, no sign-in

Claude — web, desktop & Code every tier, incl. free
  1. Customize → Connectors → Add custom connector
  2. Remote MCP server URL: https://freemcp.globalgov.io/mcp
  3. Free plan allows one custom connector. Claude Code: claude mcp add --transport http globalgov https://freemcp.globalgov.io/mcp
  4. For full depth, use https://mcp.globalgov.io and "Sign in with GlobalGov" (leave OAuth Client ID / Secret blank — auto-registration)
Cursor / Windsurf / Cline
  1. Add a remote Streamable HTTP MCP server
  2. URL: https://freemcp.globalgov.io/mcp (Windsurf/Cline: type streamable-http)
  3. Full depth: add a headers entry X-API-Key: gg_your_key and point at https://mcp.globalgov.io
VS Code + GitHub Copilot incl. free Copilot plan
  1. Command Palette → MCP: Open User Configuration
  2. Add "globalgov": { "type": "http", "url": "https://freemcp.globalgov.io/mcp" }
  3. OAuth for the full-access URL is handled automatically (browser opens on first connect)
Gemini CLI
  1. gemini mcp add --transport http globalgov https://freemcp.globalgov.io/mcp
  2. Or add "globalgov": {"httpUrl": "https://freemcp.globalgov.io/mcp"} under mcpServers
Mistral Le Chat incl. free (account owner)
  1. Connectors → add MCP connector
  2. URL: https://freemcp.globalgov.io/mcp (HTTPS, no auth)

Paid-plan assistants

ChatGPT Plus / Pro / Business / Enterprise — not free
  1. Turn on Developer mode in Settings
  2. Connectors/Apps → create → paste URL https://freemcp.globalgov.io/mcp
  3. Free ChatGPT cannot add custom remote connectors
Grok SuperGrok / Premium+ — not free
  1. grok.com/connectorsNew Connector → Custom
  2. URL: https://freemcp.globalgov.io/mcp
Perplexity Pro / Max / Enterprise
  1. Connectors → add custom connector
  2. Transport Streamable HTTP, auth None, URL https://freemcp.globalgov.io/mcp
  3. Full depth: choose auth API Key and paste your X-API-Key

Every path hits the same tools. The free URL is the zero-config default; X-API-Key and OAuth on https://mcp.globalgov.io unlock full depth for scripts and assistants respectively.

Prefer the command line? With MCP tooling you can smoke-test the server directly:

bash
npx @modelcontextprotocol/inspector
#   URL: https://mcp.globalgov.io   (runs the OAuth "Sign in with GlobalGov" flow)
#   or the keyless public twin: https://mcp.globalgov.io/public
#   or add an X-API-Key header to https://mcp.globalgov.io for BYO-key access

Free tier vs. paid tier

Access is resolved from your X-API-Key:

ToolNo key (free)Key with API access
get_platform_stats
search_solicitationsTeaser: page 1, up to 5 resultsFull results, 20/page, pagination
get_solicitation
get_related_solicitations
get_live_opportunity_feed

Access is resolved live on every call from your identity (OAuth sign-in or X-API-Key): a plan with API access gets all 5 tools, a free/trial account gets the 2 teaser tools. Calling a paid tool without a qualifying plan returns a tool error error="unavailable_for_tier" (not a crash). Anonymous callers on the public twin get the teaser too. Entitlement is never frozen in the token — downgrading a plan drops it back to the teaser immediately.

Free search shares the public site's per-visitor budget (a handful of searches per 24h); a paid key lifts that and is governed by the per-key hourly limit instead. See Rate limits.

The five tools

ToolAccessParameters
search_solicitationsFree tierquery* (2–200), country (ISO-2), category (enum), page
get_platform_statsFree tiernone
get_solicitationAPI keyslug_or_id* (slug or UUID)
get_related_solicitationsAPI keyslug*
get_live_opportunity_feedAPI keynone

* required. Category enum: DEFENSE_SECURITY, INFRASTRUCTURE, HEALTH_SERVICES, ENERGY, TECHNOLOGY, PROFESSIONAL_SERVICES, CONSTRUCTION, LOGISTICS, HUMANITARIAN, ENVIRONMENTAL, RESEARCH_DEVELOPMENT, EDUCATION, OTHER.

search_solicitations

Full-text search over the corpus. Free callers get a teaser (teaser=true, up to 5 results, page 1) with the true match total; keyed callers get 20/page with pagination. If total_degraded=true, the exact count timed out and total is a floor.

json
POST https://mcp.globalgov.io
Content-Type: application/json
X-API-Key: gg_your_key_here

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search_solicitations",
    "arguments": { "query": "cybersecurity training", "country": "US", "page": 1 }
  }
}

The result carries JSON in both the text content block and structuredContent:

json
{
  "results": [
    {
      "id": "6a3b7032-4989-4691-8465-9829d3306151",
      "title": "Cybersecurity Awareness Training Services",
      "agency_name": "Department of Homeland Security",
      "country_code": "US",
      "value_usd": 1250000.0,
      "currency": "USD",
      "deadline": "2026-08-15T17:00:00+00:00",
      "category": "DEFENSE_SECURITY",
      "slug": "dhs-cybersecurity-awareness-training-...",
      "description": "...",
      "rank": 0.8213
    }
  ],
  "total": 3184,
  "page": 1,
  "has_more": true,
  "total_degraded": false
}

get_platform_stats

Canonical coverage numbers, pre-computed and served from cache. No arguments. Every field:

FieldTypeWhat it is
solicitations_totalintFull searchable corpus. Records, not open opportunities.
countries_with_national_sourceintCountries whose own procurement system is indexed. The figure to cite.
countries_with_any_recordintCountries holding any record, including those reached only through multilateral feeds. Breadth, not coverage.
countries_measured_atdateWhen both country counts were derived.
countries_definitionstringStates which codes are counted and which are excluded by rule.
source_systems_with_dataintDistinct source systems carrying records.
languages_supportedintDistinct languages held and normalized to English on read.
languages_measured_atdateWhen that count was measured.
languages_definitionstringStates what the count folds and what it excludes.
vendors_indexedintCanonical (deduplicated) vendor records.
forecasts_trackedintTracked forecast / pre-solicitation records.
agencies_totalintRecounted live on every refresh. The example prints the last governed figure, so a live call reads higher.
solicitations_open_confirmed_deadlineintRecounted. Tenders carrying a confirmed future closing date. This is the figure to cite.
solicitations_open_including_inferredintRecounted. The above plus tenders with no published deadline seen in the last 90 days.
countries_with_openintRecounted. Countries with anything open.
open_pipeline_measured_atdateWhen the open pipeline was last computed.
open_pipeline_definitionsobjectOne prose definition per open figure, stating what share of the wider number is inferred rather than confirmed.
stats_verified_attimestampWhen the figures were MEASURED.
data_freshnessobjectWhen source data last arrived, read from ingestion runs. Compute staleness against this, never against generated_at.
generated_attimestampWhen this response was assembled. Moves on every cache refresh and says nothing about the age of the data.
retired_statsobjectFields withdrawn for being inaccurate, listed by name with the reason, so a consumer gets an explanation instead of a KeyError.
json
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": { "name": "get_platform_stats", "arguments": {} } }

An abridged response. The key set is complete and in order; the two long prose objects are shortened here and given in full by the live call:

json
{
  "solicitations_total": 10364022,
  "countries_with_national_source": 60,
  "countries_with_any_record": 193,
  "countries_measured_at": "2026-08-26",
  "countries_definition": "Countries with a national procurement source, and countries holding any record. The second includes countries reached only through multilateral feeds. …",
  "source_systems_with_data": 70,
  "languages_supported": 40,
  "languages_measured_at": "2026-08-21",
  "languages_definition": "Distinct languages present in the corpus and normalized to English on read. …",
  "vendors_indexed": 2958399,
  "forecasts_tracked": 1849,
  "agencies_total": 184564,
  "solicitations_open_confirmed_deadline": …,
  "solicitations_open_including_inferred": …,
  "countries_with_open": …,
  "open_pipeline_measured_at": "2026-08-15",
  "open_pipeline_definitions": { "…": "…" },
  "stats_verified_at": "2026-08-14T00:00:00+00:00",
  "data_freshness": { "available": true, "…": "…" },
  "generated_at": "2026-08-23T02:06:56+00:00",
  "retired_stats": {
    "fields": ["solicitations_active",
               "solicitations_open_for_bidding",
               "open_with_confirmed_deadline",
               "countries_live_coverage",
               "source_systems_active",
               "market_coverage_usd"],
    "retired_on": "2026-08-14",
    "reason": "Withdrawn for being inaccurate, not stale. …"
  }
}

The four counts are recounted on every refresh, so they are described in the table rather than frozen into an example that would be wrong by the time you read it. The other figures are the governed values measured on the dates the response itself carries. Nothing in this response is a number we cannot show you the derivation of, which is the point of stats_verified_at, data_freshness and retired_stats all being separate fields.

get_solicitation API key

Full record by slug (preferred) or UUID — original + English title, agency, country, value/currency, deadline, status, category, NAICS/CPV, source_url, full description, and (when available) an AI-generated brief. Returns error="not_found" for unknown ids.

json
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": { "name": "get_solicitation",
              "arguments": { "slug_or_id": "dhs-cybersecurity-awareness-training-..." } } }

get_related_solicitations API key

Up to 5 open solicitations related to a record (matched by country + NAICS, nearest deadline first). Argument: slug.

get_live_opportunity_feed API key

A random sample of 12 records from the curated live feed of open opportunities. No arguments. Served from a background-refreshed cache; if cold, returns a retryable warming_up error rather than an empty list.

Honest degraded states

GlobalGov never fakes an empty result. When a cache is cold or the database is under load, a tool returns an isError result whose JSON payload has a machine error code and a retryable flag:

errorMeaningRetryable
unavailable_for_tierTool needs a key on a plan with API accessNo
warming_upBacking cache cold / warm scheduledYes
computingStats being recalculatedYes
rate_limit_exceededAccess-level limit hit (carries reset_at)Yes
not_foundUnknown slug/idNo
Live

REST API reference

The REST API serves the same corpus over plain HTTPS for back-end integrations. Two surfaces are live today:

  • Public read endpoints — no key required, rate-limited per visitor. These power the MCP search tools.
  • Key-authenticated endpoints — an X-API-Key on a plan with API access lifts the anonymous limits and unlocks the list endpoints below.

Base URL: https://globalgov.io/api/v1. All responses are JSON.

Building a new integration? Prefer the Developer API (REST v1) at https://globalgov.io/api/public/v1 — it's the versioned, contract-stable surface with explicit schemas, opaque IDs, cursor pagination, and scoped ggk_ keys, and it's live now. The endpoints below remain available for existing callers.

Authentication

Send your key in the X-API-Key header. Public /public/* read endpoints work without one.

bash
curl "https://globalgov.io/api/v1/solicitations?limit=25" \
  -H "X-API-Key: gg_your_key_here"

Rate limits

CallerLimitOn exceed
Anonymous /public/search~7 searches / 24h per visitor429 with reset_at
API key on /api/v1/*1,000 requests / hour429 with Retry-After header

Error format

Errors use the standard FastAPI shape with an HTTP status and a detail field:

json
// 429 — anonymous search budget exhausted
{ "detail": { "error": "rate_limit_exceeded",
              "searches_remaining": 0,
              "reset_at": "2026-07-21T00:19:59+00:00" } }

// 401 invalid key · 403 no API access · 404 not found
{ "detail": "Invalid API key" }

The Developer API (REST v1) uses the richer RFC 9457 problem+json error format with stable machine codes and a request_id — it is live now.

Public endpoints Free tier

No authentication. Rate-limited per visitor.

GET/public/stats

Platform coverage numbers (same payload as the get_platform_stats MCP tool).

bash
curl "https://globalgov.io/api/v1/public/stats"
GET/public/search

Full-text search. Query params: q* (2–200), country (ISO-2), category (enum), page (≥1), limit (1–20, default 20). Returns { results[], total, page, has_more, total_degraded }; each result has id, title, agency_name, country_code, value_usd, currency, deadline, category, slug, description, rank.

bash
curl "https://globalgov.io/api/v1/public/search?q=cybersecurity&country=US&limit=5"
GET/public/solicitations/{slug}

One full record by slug or UUID — includes original + English title, agency, country, value, deadline, status, NAICS/CPV, source_url, description, and an ai_brief when available. 404 if unknown.

bash
curl "https://globalgov.io/api/v1/public/solicitations/ted-pl-poland-medical-consumables-...-291518-2025"
GET/public/solicitations/{slug}/related

Up to 5 related open solicitations (by country + NAICS). Returns { related: [{ slug, title, country_name, agency_name, deadline, status }] }.

bash
curl "https://globalgov.io/api/v1/public/solicitations/{slug}/related"

Key-authenticated endpoints API key

Require an X-API-Key on a plan with API access. List endpoints accept limit (≤100, default 25) and offset, and return { data: [...], total, has_more }.

MethodPathReturns
GET/solicitationsActive solicitations (paged)
GET/solicitations/{id}One solicitation by UUID
GET/awardsContract awards (paged)
GET/vendorsVendors (paged)
GET/agenciesAgencies (paged)
GET/pipelineYour organization's pipeline (paged)
bash
curl "https://globalgov.io/api/v1/solicitations?limit=25&offset=0" \
  -H "X-API-Key: gg_your_key_here"

These endpoints return the full record objects. For a field-explicit, versioned contract with opaque IDs and cursor pagination, use the Developer API (REST v1) — live now — or the public / MCP surfaces.

Live

Developer API (REST v1)

A versioned, read-only REST API over the full corpus — live now. Explicit response schemas (never raw records), opaque non-sequential IDs, cursor pagination, and RFC 9457 errors.

Base URLhttps://globalgov.io/api/public/v1
AuthAuthorization: Bearer ggk_live_… (scoped API key)
OpenAPIopenapi.json · interactive Swagger UI
AccessRequires the API & MCP add-on ($400/mo, Global or Enterprise+ plans)

Get a key

In Settings → API & MCP Access generate a key (shown once, format ggk_live_…) — or a rate-limited sandbox key (environment: "test"). Keys carry least-privilege scopes: solicitations:read, awards:read, vendors:read, agencies:read. Keys are stored hashed, verified by an indexed O(1) lookup, and independently revocable.

bash
curl "https://globalgov.io/api/public/v1/solicitations?limit=25&country=US" \
  -H "Authorization: Bearer ggk_live_your_key"

Endpoints

Every resource has a cursor-paginated list and a by-id lookup. IDs are opaque and non-sequential.

MethodPathScope
GET/solicitations · /solicitations/{id}solicitations:read
GET/awards · /awards/{id}awards:read
GET/vendors · /vendors/{id}vendors:read
GET/agencies · /agencies/{id}agencies:read
GET/pingany

List params: limit (1–100, default 25), cursor (opaque), plus per-resource filters (e.g. country, naics, posted_after, active). Response envelope: { "data": […], "next_cursor": "…", "has_more": true }. Corpus results are scoped to your subscribed regions; no personal contact data is ever returned.

Errors & rate limits

Errors are application/problem+json (RFC 9457) with a stable machine code and a request_id: unauthorized, invalid_key, insufficient_scope, insufficient_tier, not_found, validation_error, rate_limited. Every response carries RateLimit-Limit/Remaining/Reset; live keys default to 1,000 req/hr, sandbox keys 60 req/hr.

On the roadmap

Live today: the four read resources above, scoped keys, sandbox keys, cursor pagination, and the OpenAPI spec. Still coming: Python/TypeScript SDKs, write access to your own pipeline/watchlist, a dedicated api.globalgov.io host, and a published 6-month deprecation policy. Tell us what you need next.

Support

Questions, a key that isn't working, or a feature request? Contact us or manage your key anytime in Settings → API & MCP Access.