Skip to content
TenderGraph

Machine surfaces

TenderGraph API & MCP server

One dataset, two machine surfaces: a REST API and an MCP server. Same records, same field names, provenance everywhere. Your API key lives under API keys in your account — keys look like tg_live_….

Connect your AI

One MCP server, every major assistant. The endpoint is https://tendergraph.ai/api/mcp (Streamable HTTP). Tools: search_tenders, get_tender, qualify_tender, analyze_awards, create_monitor/check_monitor, search_companies/get_company, list_sources, plus the search/fetch deep-research pair — fifteen in all.

Two ways in. OAuth sign-in — add the endpoint, approve once in the browser, nothing to copy — for Claude and ChatGPT, which support it. An API key — as an Authorization: Bearer header where the client can send one, or in the endpoint URL where it cannot — for Perplexity, Grok, scripts and both APIs. Either way the connector sees the same fifteen tools and the same account.

Claude (claude.ai, Desktop, Claude Code)

Settings → Connectors → “Add custom connector” → paste the endpoint → Connect. Claude opens a TenderGraph sign-in; approve it and you are done. No key to copy.

https://tendergraph.ai/api/mcp

Prefer a key? Paste the endpoint with your key on it instead. Treat that URL as a secret: it carries the key.

https://tendergraph.ai/api/mcp?key=tg_live_YOUR_KEY

Claude Code (it opens the browser to sign in; add the header to use a key instead):

claude mcp add --transport http tendergraph "https://tendergraph.ai/api/mcp"

ChatGPT

Settings → Apps & Connectors → Advanced → enable Developer mode (Plus, Pro or Business, on the web app), then Create connector: paste the endpoint, choose OAuth, and approve the TenderGraph sign-in when ChatGPT opens it.

https://tendergraph.ai/api/mcp

Prefer a key? ChatGPT refuses a key in a query string but accepts one in the path: use the key-in-path endpoint with “No authentication”. Treat that URL as a secret.

https://tendergraph.ai/api/mcp/tg_live_YOUR_KEY

The search/fetch pair follows OpenAI's deep-research result contract, so the same connector works as a research source. Building on the OpenAI API instead? Pass the MCP tool with authorization: "tg_live_YOUR_KEY" in the Responses API — bearer keys work there directly.

Perplexity

Pro/Max/Enterprise: Account Settings → Connectors → “+ Custom connector” → Remote. Server URL https://tendergraph.ai/api/mcp, auth type API Key, value tg_live_YOUR_KEY. Enable the connector as a source in a thread, then ask it for live tenders.

Grok (xAI)

grok.com → Connectors → New Connector → Custom → server URL https://tendergraph.ai/api/mcp, completing auth with your tg_live_… key when prompted (use the key-in-path URL from the ChatGPT steps if the dialog has no key field). On the xAI API, pass {type:"mcp", server_url, authorization:"tg_live_YOUR_KEY"}.

Anything that speaks stdio

{
  "mcpServers": {
    "tendergraph": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://tendergraph.ai/api/mcp",
        "--header", "Authorization: Bearer tg_live_YOUR_KEY"
      ]
    }
  }
}

Then ask: “Find live tenders for commercial lifting equipment inspection in the UK, qualify the top three against my profile, and set up a monitor for new ones.”

REST API

OpenAPI spec at /api/v1/openapi.

Search

curl -s "https://tendergraph.ai/api/v1/tenders?q=scaffolding&status=open&region=UKD&per_page=5" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Filters: q (websearch syntax), status, notice_type, country (ISO alpha-2 — any of the 53 covered: GB, IE, FR, DE, IT, NL, ES, BE, PL, RO, CZ, BG, SE, DK, FI, AT, PT, GR, HU, SK, SI, HR, LT, LV, EE, LU, MT, CY, NO, CH, IS, LI, UA, MK, US, CA, CO, MX, BR, CL, UY, PY, KR, JP, AU, NZ, SG, TW, IL, SA, ZA, NG, TZ), source, cpv (prefixes, e.g. 45=construction), region (ITL prefixes, e.g. UKD, UKM, IE), buyer, published_from/to, deadline_from/to, expires_from/to (the published contract end date — with notice_type=award and sort=expires_asc, the incumbents' contracts coming up for renewal, as on /expiring), value_from/to, framework, sort (published_desc · deadline_asc · value_desc · relevance · expires_asc), page, per_page (≤100), dedupe (default true — collapses to one row per procurement). Add format=csv for the same query as an Excel-ready file (UTF-8 BOM, ISO dates, one currency column, up to 10,000 rows — the columns are documented in the OpenAPI spec; also on /analytics/awards, /companies/{key} and a monitor check).

Detail

curl -s "https://tendergraph.ai/api/v1/tenders/TENDER_ID?include_raw=true" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Full record: description, documents — with text excerpts where the document has been extracted (open tenders' documents are extracted first; most attachments of closed and historical notices are not yet) — related notices for the same procurement, and (with include_raw) the verbatim source release for audit, except where the notice names a private individual by a personal identifier.

On-demand document summaries

curl -s -X POST "https://tendergraph.ai/api/v1/tenders/TENDER_ID/summarize" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Claude-generated structured summaries (key dates, requirements, evaluation criteria, contacts) for a tender's extracted documents. Strictly on request, never automatic, and bounded by a server-side daily cap.

Monitors — “what's new since I last asked”

# save a profile
curl -s -X POST "https://tendergraph.ai/api/v1/monitors" \
  -H "Authorization: Bearer tg_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"name": "NW construction", "query": {"cpv": ["45"], "region": ["UKD"], "status": ["open"]}}'

# consume what's new (advances the watermark)
curl -s -X POST "https://tendergraph.ai/api/v1/monitors/MONITOR_ID/check" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

# peek without consuming
curl -s "https://tendergraph.ai/api/v1/monitors/MONITOR_ID/check" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Each check returns only tenders first seen since your previous check — nothing missed, nothing repeated. The same monitors are available to agents as the create_monitor / check_monitor MCP tools, so a Claude session can pick up exactly where the last one left off.

Webhooks — push instead of poll

curl -s -X POST "https://tendergraph.ai/api/v1/monitors" \
  -H "Authorization: Bearer tg_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"name": "NW construction", "query": {"cpv": ["45"], "region": ["UKD"]},
       "webhook_url": "https://your-app.example/hooks/tendergraph"}'

New matches POST to your endpoint on the next delivery run after we first see them, HMAC-signed with the webhook_secret the create response shows once — verify X-TenderGraph-Signature as sha256=HMAC(secret, body). The cursor advances only on your 2xx, so a down endpoint never loses a notice: the same batch retries. At-least-once delivery; dedupe on tender id.

Qualification — bid / no-bid with evidence

# store the company profile once
curl -s -X POST "https://tendergraph.ai/api/v1/profiles" \
  -H "Authorization: Bearer tg_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"name": "WHS lifting", "profile": {"cpv": ["4522", "5075"], "regions": ["UKD"],
       "keywords": ["scaffolding", "LOLER", "lifting"], "value_min": 20000, "value_max": 500000}}'

# score any tender against it
curl -s -X POST "https://tendergraph.ai/api/v1/qualify" \
  -H "Authorization: Bearer tg_live_YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"tender_id": "TENDER_ID", "profile_id": "PROFILE_ID"}'

Deterministic scoring across CPV fit, region fit, capability keywords, value band and deadline runway — every point carries its evidence, unknowns are named rather than guessed, and the same input always gives the same answer. Free and instant; agents get it as the qualify_tender MCP tool and layer judgement over the extracted documents on top.

Awards analytics — the historical layer

curl -s "https://tendergraph.ai/api/v1/analytics/awards?cpv=45&region=UKD&awarded_from=2025-01-01" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Totals, top winning suppliers, top buyers, monthly trend and sector split over the award history — incumbent analysis and market sizing in one call. History runs as deep as each source publishes it; the coverage page gives each source's range. Agents: analyze_awards.

Company lookup — who wins the work

# find a supplier: yours, a competitor, an incumbent
curl -s "https://tendergraph.ai/api/v1/companies?q=Kier" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

# their full history: value by year, top buyers, sector split, recent awards
curl -s "https://tendergraph.ai/api/v1/companies/kier%20construction" \
  -H "Authorization: Bearer tg_live_YOUR_KEY"

Every published spelling of a company is grouped under one key while separate legal entities stay separate. Value is attributed only where the publisher stated it — an itemised per-supplier amount, or the whole value where one supplier was named. Awards naming several suppliers with no published split are counted but never valued, and sterling and euro are reported side by side, never summed. Agents: search_companies and get_company.

Public endpoints (no key)

GET https://tendergraph.ai/api/v1/stats      # store totals
GET https://tendergraph.ai/api/v1/sources    # coverage registry, licences, last successful sync
GET https://tendergraph.ai/.well-known/mcp.json

The record format

Every tender serialises the same way, whatever the source. Stable names; additive changes only.

{
  "id": "…",
  "title": "Wilford Hill Crematorium — Replacement & Maintenance",
  "buyer": { "name": "Nottingham City Council", "id": "GB-…" },
  "notice_type": "opportunity",        // planning | opportunity | award | update
  "status": "open",                    // open | closed | awarded | cancelled | planned
  "value": { "min": null, "max": 2613500, "currency": "GBP" },
  "cpv": [ { "code": "45215000", "label": "Construction work" } ],
  "regions": [ { "code": "UKF", "label": "East Midlands" } ],
  "country": "GB",
  "published_at": "…", "deadline_at": "…", "days_until_deadline": 41,
  "documents_count": 3,
  "provenance": {
    "source": "procontract", "source_notice_id": "…", "ocid": null,
    "source_url": "https://procontract.due-north.com/Advert?advertId=…",
    "first_seen_at": "…", "last_updated_at": "…",
    "licence": {                       // the source's licence, on every record
      "name": "…", "url": "…",
      "attribution": "…",              // reproduce this wherever you republish
      "share_alike": false
    }
  },
  "links": { "self": "…", "html": "…", "source": "…" }
}

Auth & limits

  • Key in Authorization: Bearer, X-Api-Key, or ?api_key= / ?key= (MCP connectors).
  • Default limit 120 requests/minute per key; 429 with a reset inside a minute.
  • Keys are hashed at rest and shown once at creation. Revoke any time under API keys in your account.
  • Data is aggregated public notices, each under its source's own licence — stated on every record as provenance.licence and listed on the coverage page. Cite provenance.source_url when you present a tender, and reproduce provenance.licence.attribution wherever you republish one.