Developer API

Momberger Data API

Airport intelligence as JSON: editions and articles, the projects database, airports, tenders and the events calendar. 69 operations a subscriber can call, 62 more for editorial accounts, one bearer token for all of them.

At a glance

REST + OpenAPI

One JSON API, described by its own 3.1 document

Bearer auth

Session tokens or long-lived mbg_live_ API keys

Entitlements

GET /v1/me tells you what your token may reach

No quota yet

No per-key request limit is enforced today

Authentication

Every request carries a bearer token. It is either a browser session token or a long-lived API key that begins mbg_live_, minted with POST /v1/account/api-keys. The plaintext key is in that one response and is never retrievable again — the API stores a hash and the first sixteen characters, so a lost key is revoked and replaced rather than recovered.

curl -H "Authorization: Bearer mbg_live_..." /v1/me

A malformed or expired token is 401. A valid token missing the entitlement is 403 — except under /v1/admin, which answers 404 to a non-admin, because a 403 would confirm that the resource exists. Errors are application/problem+json:

{ "title": "Forbidden", "status": 403, "detail": "archive is not included in this subscription" }

Endpoints

Grouped by the tag each operation carries in the spec, so the same sections appear in the OpenAPI reference. Lists take limit (maximum 200) and offset, and answer with one named array — { "articles": [...] } — with no total and no page metadata. Plural query parameters are comma-separated, never repeated: a repeated one silently keeps only the first value and returns a wider result set that looks plausible.

Identity

auth

Who the token belongs to and what it may reach. Call /v1/me first: the features array it returns is the same one the gates below are checked against, so an integration can find out what it is allowed to ask for instead of discovering it as a 403.

  • GET
    /v1/me

    The current actor and its entitlements

  • POST
    /v1/auth/magic-link

    Email a sign-in link

  • POST
    /v1/auth/session

    Exchange a sign-in link token for a session

  • DELETE
    /v1/auth/session

    Revoke the current session

The publication

editorial

Editions, the articles in them, and the archive search. This is the read path the newsletter itself is built on.

  • GET
    /v1/editions?limit&offset

    List editions, newest first

  • GET
    /v1/editions/{id}

    One edition

  • GET
    /v1/editions/{id}/articles

    Articles in an edition

  • GET
    /v1/articles/{id}

    One article

  • GET
    /v1/articles/{id}/related?limit

    The closest other articles, scored on shared topics, airports, tags and place — empty when the article carries no entity links, rather than falling back to arbitrary neighbours

  • GET
    /v1/search?q&region&terms&airport&year&edition&mine&include_events&limit&offset

    Full-text search across the archive · requires archive

  • GET
    /v1/search/facets

    Archive filter options, with counts · requires archive

  • GET
    /v1/search/export?q&region&terms&airport&year&edition&mine&include_events

    Archive results as CSV, streamed — the same filters as /v1/search, so the file matches the search, with no row cap · requires archive + exports

Airport projects

projects

The projects database: what is being built where, by whom, at what value. facets returns the filter vocabulary with counts, so a client never has to hard-code a status or a country list.

  • GET
    /v1/projects?q&country&status&region&type&terms&min_value&max_value&year_from&year_to&sort&company_id&parent_project_id&limit&offset

    Airport projects · requires projects

  • GET
    /v1/projects/facets

    Filter options for the projects list, with counts · requires projects

  • GET
    /v1/projects/{id}

    One project · requires projects

  • GET
    /v1/projects/export?q&country&status&region&type&terms&min_value&max_value&year_from&year_to&sort&company_id&parent_project_id

    Projects as CSV, streamed — takes the same filters as the list, and caps nothing · requires projects + exports

Airports

airports

An airport is addressable by database id or by IATA code, so /v1/airports/AMS works without a lookup first. Coverage and projects hang off the same reference.

  • GET
    /v1/airports?q&limit&offset

    Airports, most-covered first

  • GET
    /v1/airports/{ref}

    One airport, by id or IATA code

  • GET
    /v1/airports/{ref}/articles?limit&offset

    Coverage of one airport

  • GET
    /v1/airports/{ref}/projects

    Projects at one airport · requires projects

Worldwide Airport Tenders

tenders

Gated on the wat add-on. The list and its CSV export take the same filters, so an export matches the list that produced it. There is no category: this schema records that idea as taxonomy terms, so use terms.

  • GET
    /v1/tenders?q&region&country&type&status&terms&sort&limit&offset

    Worldwide Airport Tenders · requires wat

  • GET
    /v1/tenders/facets

    Filter options for the tenders list, with counts · requires wat

  • GET
    /v1/tenders/{id}

    One tender · requires wat

  • GET
    /v1/tenders/export?q&region&country&type&status&terms&sort

    Tenders as CSV, streamed — the same filters as the list, and it carries a `bidders` column the list does not · requires wat + exports

Events calendar

calendar

Industry events, both curated and extracted from reporting. upcoming=true drops everything in the past.

  • GET
    /v1/calendar?upcoming&limit

    Industry events, curated and reported

Reference data

reference

One call for every controlled vocabulary the write endpoints validate against: regions, countries, project types and topics. Fetch it once and cache it — it changes when a migration changes it, not per request.

  • GET
    /v1/reference

    Regions, countries, project types and topics

Reading list and watchlist

library

Per-user state: saved articles, watched projects, read history and delivery preferences. Everything here is scoped to the token's own user, so there is no id to pass and no way to read someone else's.

  • GET
    /v1/library/saved?limit&offset

    Saved articles

  • PUT
    /v1/articles/{id}/save

    Save an article

  • DELETE
    /v1/articles/{id}/save

    Remove a saved article

  • POST
    /v1/articles/{id}/view

    Record that the reader opened an article

  • GET
    /v1/library/watchlist

    Watched projects · requires projects

  • PUT
    /v1/projects/{id}/watch

    Watch a project · requires projects

  • DELETE
    /v1/projects/{id}/watch

    Stop watching a project

  • GET
    /v1/dashboard/most-read?limit

    Most read articles in the current issue

  • GET
    /v1/dashboard/recently-viewed?limit

    Pick up where you left off

  • GET
    /v1/preferences

    Delivery preferences

  • PUT
    /v1/preferences

    Update delivery preferences

Alerts and notifications

alerts

Standing queries that mail a digest when something matches, plus the status-change feed for watched projects. /v1/notifications/count is the cheap call — poll that, and fetch the list when it moves.

  • GET
    /v1/alert-rules

    Alert rules · requires alerts

  • POST
    /v1/alert-rules

    Create an alert rule · requires alerts

  • PATCH
    /v1/alert-rules/{id}

    Rename, re-time or pause an alert rule · requires alerts

  • DELETE
    /v1/alert-rules/{id}

    Delete an alert rule · requires alerts

  • GET
    /v1/notifications?unread_only&limit&offset

    Watched-project status changes

  • GET
    /v1/notifications/count

    Unread notification count for the header bell

  • POST
    /v1/notifications/read

    Mark notifications read (all, if no ids are given)

Account, seats and keys

account

The subscription's own administration. /v1/plans is the only operation on this page that needs no token at all — and it omits inactive plans, so it is a narrower list than the one on the pricing page. Key management is gated on api or mcp, either of which is enough: MCP is sold as its own add-on and uses the same bearer credential.

  • GET
    /v1/plans

    Public price list

  • GET
    /v1/account/profile

    The signed-in user's profile

  • PATCH
    /v1/account/profile

    Update name, organisation and job title

  • GET
    /v1/account/subscription

    Current plan, status, period and seats

  • GET
    /v1/account/members

    Members of this subscription

  • POST
    /v1/account/members

    Invite a colleague to a seat

  • DELETE
    /v1/account/members/{user_id}

    Remove a member from this subscription

  • GET
    /v1/account/interests

    Preferred topics, regions and airports

  • PUT
    /v1/account/interests

    Replace the whole interest set (onboarding and preferences)

  • PUT
    /v1/account/interests/terms/{slug}

    Favourite or mute one topic or tag

  • DELETE
    /v1/account/interests/terms/{slug}

    Remove a topic or tag preference

  • GET
    /v1/account/api-keys

    API keys · requires api or mcp

  • POST
    /v1/account/api-keys

    Mint an API key — the plaintext value is in the response and is never retrievable again · requires api or mcp

  • DELETE
    /v1/account/api-keys/{id}

    Revoke an API key · requires api or mcp

Imagery

media

Cover images, resolved per article. Ask for a batch by passing a comma-separated list of article ids to /v1/media/covers — one call for a whole page of rows — then fetch each image by its own id, optionally at a width with ?w=. The API resolves a pinned cover if an editor set one and derives one from the article's airport, country or company otherwise, so a client never has to decide what to show.

  • GET
    /v1/media/covers?articles

    Resolved cover imagery for a set of articles

  • GET
    /v1/media/airports?airports

    Imagery bound to a set of airports — an airport is a subject in its own right, not only a route to an article

  • GET
    /v1/media/{id}?w

    The image itself

Subscriptions and checkout

billing

Prices come from the catalogue tables, so a page cannot show a total the API would not charge: ask /v1/billing/quote rather than computing one. /v1/billing/config and /v1/billing/quote need no credential — a client reads mode first and does not offer a checkout button a deployment would answer 503 to. Mollie's own notification endpoint is not listed here; see the header of this file.

  • GET
    /v1/billing/config

    Whether checkout is available, and in which mode (test, live or disabled)

  • GET
    /v1/billing/quote?plan&currency&interval&extra_seats&add_wat

    Price a selection — the authoritative total for a plan plus add-ons

  • POST
    /v1/billing/trial

    Start the free trial — 409 if this account already has a subscription

  • POST
    /v1/billing/checkout

    Begin a payment and get the Mollie redirect — owner or admin only; 503 where this deployment cannot take payments

  • GET
    /v1/billing/checkout/{id}

    The state of one checkout. It reports; the webhook is what activates a subscription

  • POST
    /v1/billing/cancel

    Cancel at the end of the paid period — access continues to current_period_end

Invoicing and procurement

procurement

The one unauthenticated write in this API, and deliberately so: the person filling the form in works in accounts payable and has no account, so requiring one would mean "sign in to ask us to invoice you". A resubmission from the same address inside ten minutes returns the request already on file rather than filing a second one. The queue an editor works it in is admin-only.

  • POST
    /v1/procurement-requests

    Ask to be invoiced — no credential required

System

system

Unauthenticated. Reports the API's own health and whether it can reach its database.

  • GET
    /healthz

    Liveness and database readiness

Editorial endpoints

62 operations under /v1/admin, for the publication's own editors. Same bearer auth; the token's user has to be an administrator, and a token that is not gets 404 rather than 403. Summarised by resource here rather than listed one operation at a time — the request bodies are in the OpenAPI reference, and they change more often than this page could track.

Editions

5 ops
POST · PATCH · DELETE /v1/admin/editions

Create a draft, update it, publish it, delete it, and bulk-import a historical issue from a pasted export (POST /v1/admin/editions/import).

Newsletter

4 ops
GET · POST /v1/admin/editions/{id}/newsletter/…

Dry-run an edition's digest to see who would receive it and send nothing, send one copy to a named address, queue it for every entitled subscriber, or read back its delivery state — sent, queued, failed, dead.

Articles

3 ops
POST · PATCH · DELETE /v1/admin/articles

Add an article to an edition, edit it, remove it.

Article entity links

4 ops
GET · PUT /v1/admin/articles/{id}/…

Replace the set of topics and tags, airports, or companies attached to an article, and read all three back together.

Article ↔ project links

4 ops
GET · PUT · POST /v1/admin/articles/{id}/projects

Read and replace an article's project links, ask for ranked candidate matches, or auto-link every confident one.

Article covers

2 ops
PUT · DELETE /v1/admin/articles/{id}/cover

Pin an image to an article, or release it back to the cover the API resolves automatically from the airport, country or company.

Media library

5 ops
GET · POST · PATCH · DELETE /v1/admin/media

Upload, relabel and delete image assets. The list can be filtered to the ones bound to nothing, which is how orphans get found.

Projects

7 ops
POST · PATCH · DELETE · PUT /v1/admin/projects

Full CRUD, plus three replace-the-set endpoints for a project's airports, companies and terms, and one that reads all three back.

Tenders

8 ops
GET · POST · PATCH · DELETE · PUT /v1/admin/tenders

The same shape as projects, plus a read of one tender in any state — including unpublished.

Editorial inbox

6 ops
GET · PATCH · POST /v1/admin/inbox

Pre-screened RSS candidates, highest score first. Approve, skip, reopen, bulk-skip, or promote one into a draft article. Nothing here publishes itself: promotion produces a draft, and a person publishes it.

News sources

5 ops
GET · POST · PATCH · DELETE /v1/admin/sources

The ingest whitelist, plus POST /v1/admin/sources/poll to run every enabled source now.

Calendar

4 ops
GET · POST · PATCH · DELETE /v1/admin/calendar

Event CRUD, and a list that includes events already in the past.

Metadata suggestion

1 ops
POST /v1/admin/suggest/metadata

Extracts topics, tags, airports, region and country from a block of text. It suggests; it never writes to an article.

Email

1 ops
GET /v1/admin/email/usage

Outbox depth, dead letters, and what has been sent in the last hour and day against the configured send budget.

Procurement

3 ops
GET · POST /v1/admin/procurement

The queue behind POST /v1/procurement-requests: a filtered list, one request with its review trail, and a review that appends to that trail. The review is a POST rather than a PATCH because sending it twice records two reviews — it is not idempotent and should not look like it is.

Agent-ready spec

Paste this whole block into Claude, ChatGPT, Cursor or any coding agent. It is self-contained: auth, the response envelope, pagination, the entitlement model, every query parameter that is easy to get wrong, and curl examples that run against this deployment.

# Momberger Airport Information — REST API

Base URL: 
Spec:     /openapi.json          (OpenAPI 3.1)
Docs:     /docs                   (the same spec, rendered)
  The spec is the contract. This block is a hand-written summary of it and the
  API is under active development, so fetch /openapi.json if the two disagree.

AUTH
  Every request except GET /healthz and GET /v1/plans:
    Authorization: Bearer <token>
  <token> is either a browser session token or a long-lived API key beginning
  `mbg_live_`. Mint a key with POST /v1/account/api-keys, which needs
  the api or the mcp entitlement. The plaintext value is in that one response
  and is never retrievable again — the API keeps a hash and the first
  16 characters.
  A malformed or expired token is 401. A valid token that lacks the entitlement
  is 403 — except under /v1/admin, which answers 404 to a non-admin so that the
  response does not confirm the resource exists.

CONTENT
  Requests:  application/json
  Responses: application/json
  Errors:    application/problem+json (RFC 9457)
             { "title": "...", "status": 403, "detail": "..." }

RESPONSE SHAPE
  A list endpoint answers with one named array, not a generic envelope:
    GET /v1/editions          -> { "editions":      [...] }
    GET /v1/search            -> { "articles":      [...] }
    GET /v1/projects          -> { "projects":      [...] }
    GET /v1/airports          -> { "airports":      [...] }
    GET /v1/tenders           -> { "tenders":       [...] }
    GET /v1/calendar          -> { "events":        [...] }
    GET /v1/alert-rules       -> { "rules":         [...] }
    GET /v1/notifications     -> { "notifications": [...], "unread": 3 }
    GET /v1/account/api-keys  -> { "keys":          [...] }
    GET /v1/plans             -> { "plans":         [...] }
  An empty collection may come back as null rather than []. Normalise it.
  There is no total count and no page metadata anywhere: page by asking for
  limit+1 rows and seeing whether you got them.

PAGINATION
  limit  (default varies by endpoint, maximum 200)
  offset (default 0)
  A limit above 200 is rejected with 422 and a detail of "validation failed",
  which names nothing — so clamp client-side.

LIST FILTERS ARE COMMA-SEPARATED, NOT REPEATED
  ?terms=capex,ppp        correct
  ?terms=capex&terms=ppp  silently keeps only the first, and returns a WIDER
                          result set that looks plausible
  This applies to every plural query parameter.

ENTITLEMENTS
  GET /v1/me returns { user_id, subscription_id, is_admin, features[] }.
  features[] is drawn from: archive, projects, alerts, wat, api, mcp, exports,
  ai_assistant. Check it before offering a call rather than handling the 403.
    archive   -> /v1/search, /v1/search/facets
    projects  -> /v1/projects*
    alerts    -> /v1/alert-rules*
    wat       -> /v1/tenders*
    exports   -> the three CSV endpoints, in addition to the gate covering the
                 content: projects+exports, wat+exports, archive+exports.
                 Holding one without the other is a real state, so both are
                 checked, and the download button is hidden on exports.
  Admins carry every feature.
  A trial is a third answer, not a missing one: /v1/me also returns previews[],
  and a gate listed there is narrowed rather than refused — the archive to the
  latest published edition, projects to five rows, tenders to three.

READING THE PUBLICATION
  GET /v1/editions?limit=&offset=
      Editions newest first: id, number, publish_date, status, summary,
      top_developments[], key_trends[], article_count.
  GET /v1/editions/{id}
  GET /v1/editions/{id}/articles
      An article carries: id, headline, slug, content, takeaways[],
      why_it_matters, region, region_slug, country_code, source_name,
      edition_id, publish_date, featured, is_saved.
  GET /v1/articles/{id}
  GET /v1/articles/{id}/related?limit=      (default 4)
      Scored on shared topics, airports, tags and place. An article carrying no
      entity links returns an empty list — the honest answer, not four
      arbitrary stories from the same region.

  GET /v1/search
      q               websearch syntax — quoted phrases and OR are supported
      region          region SLUG, not the display name
      terms           comma-separated topic or tag slugs. ALL must match:
                      terms=a,b means both, never either.
      airport         airport reference (database id or IATA code)
      year            four-digit year, matched on the edition's publish date
      edition         edition NUMBER, not its id
      mine            true restricts to the reader's favourite topics,
                      regions and airports
      include_events  calendar events are excluded by default — they are a
                      calendar, not reporting. true folds them in.
      limit           default 50, maximum 200
      offset          default 0
  GET /v1/search/facets
      The same filter vocabulary with counts ({ value, label, count }), so a
      client never hard-codes a region or topic list.
  GET /v1/search/export                       [archive + exports]
      The same filters, as text/csv, streamed and uncapped. Headline, why it
      matters, topics, source and a link — not the article text.

AIRPORT PROJECTS
  GET /v1/projects
      q                  free text
      country            ISO 3166-1 alpha-2 country code
      status             planned | ongoing | complete | cancelled | on_hold
      region             region slug, as /v1/projects/facets reports it
      type               project type slug, from the same facets
      terms              COMMA-SEPARATED topic or tag slugs. ALL must match, so
                         terms=cargo,technology narrows rather than widens. A
                         repeated ?terms=a&terms=b keeps only the first.
      min_value          lowest reported value. NOT converted to a common
      max_value          currency: each project carries the amount its source
                         stated, in value_currency. A project with no stated
                         value is excluded by either bound.
      year_from          expected completion year, inclusive
      year_to
      sort               title_asc (default) | value_desc | value_asc |
                         year_desc | year_asc | airport_asc | updated_desc
      company_id         uuid; resolves through aliases and subsidiaries.
                         A malformed one is 400, an unmatched one is [].
      parent_project_id  uuid; the packages under a programme
      limit              default 50, maximum 200
      offset             default 0
      A project carries: id, title, description, airport, iata_code,
      country_code, region, project_type, status, value_amount, value_currency,
      value_basis, announced_on, expected_completion_year, parent_project_id,
      is_watched.
  GET /v1/projects/facets
  GET /v1/projects/{id}
  GET /v1/projects/export                     [projects + exports]
      The same filters, as text/csv, streamed and uncapped. Ask for the
      export rather than paging /v1/projects to build one — there is no row
      limit here, and a truncated export is indistinguishable from a complete
      one, which is the bug this endpoint exists to avoid.

AIRPORTS
  GET /v1/airports?q=&limit=&offset=
  GET /v1/airports/{ref}                 ref is a database id OR an IATA code
  GET /v1/airports/{ref}/articles?limit=&offset=
  GET /v1/airports/{ref}/projects

TENDERS                                       [requires the wat entitlement]
  GET /v1/tenders
      q         free text
      region    region slug, as /v1/tenders/facets reports it
      country   ISO 3166-1 alpha-2 country code
      type      rfp | tender | framework | eoi
      status    open | closing_soon | closed | awarded | cancelled
      terms     COMMA-SEPARATED topic or tag slugs; ALL must match. This is
                where production's "category" went — there is no separate
                category vocabulary here.
      sort      deadline_asc (default) | deadline_desc | value_desc |
                published_desc
      limit     default 50, maximum 200
      offset    default 0
  GET /v1/tenders/facets
      The same vocabulary with counts. topics stands in for categories.
  GET /v1/tenders/{id}
  GET /v1/tenders/export                      [wat + exports]
      The same filters, as text/csv, streamed and uncapped. It also carries a
      "bidders" column the list does not — who bid and lost is the buying
      signal, and it is only in the export.

EVENTS
  GET /v1/calendar?upcoming=&limit=
      upcoming defaults to TRUE, so the past is hidden unless you ask.
      limit defaults to 200. Answers { "events": [...] }.

REFERENCE
  GET /v1/reference
      Regions, countries, project types and topics — every controlled
      vocabulary the write endpoints validate against. Cache it.

PER-USER STATE
  GET    /v1/library/saved?limit=&offset=
  PUT    /v1/articles/{id}/save
  DELETE /v1/articles/{id}/save
  POST   /v1/articles/{id}/view
  GET    /v1/library/watchlist
  PUT    /v1/projects/{id}/watch
  DELETE /v1/projects/{id}/watch
  GET    /v1/dashboard/most-read?limit=
  GET    /v1/dashboard/recently-viewed?limit=
  GET    /v1/preferences
  PUT    /v1/preferences
      digest_frequency, digest_format, email_headlines_only, attach_pdf

ALERTS                                     [requires the alerts entitlement]
  GET    /v1/alert-rules
  POST   /v1/alert-rules
  PATCH  /v1/alert-rules/{id}
  DELETE /v1/alert-rules/{id}
  GET    /v1/notifications?unread_only=&limit=&offset=
  GET    /v1/notifications/count
  POST   /v1/notifications/read

ACCOUNT
  GET    /v1/plans                           no token required.
         Inactive plans are omitted, and the price book travels with each plan
         (per currency and interval) along with the buyable add-ons. Enterprise
         is inactive and carries no prices, so it cannot be checked out.
  GET    /v1/account/profile
  PATCH  /v1/account/profile
  GET    /v1/account/subscription
  GET    /v1/account/members
  POST   /v1/account/members
  DELETE /v1/account/members/{user_id}
  GET    /v1/account/interests
  PUT    /v1/account/interests
  PUT    /v1/account/interests/terms/{slug}
  DELETE /v1/account/interests/terms/{slug}
  GET    /v1/account/api-keys
  POST   /v1/account/api-keys
  DELETE /v1/account/api-keys/{id}

IMAGERY
  GET /v1/media/covers?articles=<id>,<id>,<id>
      Resolved covers for a batch of articles in one call. Pinned if an editor
      set one, derived from the airport/country/company otherwise.
  GET /v1/media/airports?airports=<id>,<id>
      Imagery bound to airports themselves, for an airport profile — absent is
      normal, and the caller draws its own cover from the airport id.
  GET /v1/media/{id}?w=
      The image bytes. ?w= is a closed ladder — 144, 288, 320, 640, 960, 1280 —
      and anything else is a 400, because an arbitrary width is both a
      cache-buster and a decode per request. Omit it for the original.

SUBSCRIPTIONS AND CHECKOUT
  GET  /v1/billing/config                    no token required.
       { checkout_enabled, mode } where mode is test | live | disabled. Read it
       before offering a checkout button rather than offering one that 503s.
  GET  /v1/billing/quote?plan=&currency=&interval=&extra_seats=&add_wat=
       no token required. The authoritative total. Every figure comes from the
       catalogue tables, so a page cannot show a price we would not charge.
  POST /v1/billing/trial                     409 if this account already has
                                             a subscription.
  POST /v1/billing/checkout                  owner or admin member. Returns a
       Mollie redirect URL, which is not stored anywhere and expires.
  GET  /v1/billing/checkout/{id}             the state of one checkout. It
       reports; the provider's notification is what activates a subscription,
       whether or not anyone returns to the page. 404, never 403, for a
       well-formed id belonging to someone else.
  POST /v1/billing/cancel                    owner only. Access continues to
       current_period_end, because that period is paid for.

INVOICING
  POST /v1/procurement-requests              no token required.
       For an organisation that cannot pay by card. A resubmission from the
       same address inside ten minutes returns the request already on file
       rather than filing a second one.

ADMIN                                          [editorial accounts, 404 to others]
  /v1/admin/editions (and their newsletter dry-run, test, send and stats),
  /v1/admin/articles (and their terms, airports, companies, project links and
  covers), /v1/admin/media, /v1/admin/projects, /v1/admin/tenders,
  /v1/admin/inbox, /v1/admin/sources, /v1/admin/calendar,
  /v1/admin/procurement, /v1/admin/suggest/metadata, /v1/admin/email/usage
  62 operations. See /docs for their request bodies.

EXAMPLES

# 1. What am I allowed to call?
curl -H "Authorization: Bearer mbg_live_..." /v1/me

# 2. The newest edition and everything in it
curl -H "Authorization: Bearer mbg_live_..." "/v1/editions?limit=1"
curl -H "Authorization: Bearer mbg_live_..." /v1/editions/<id>/articles

# 3. Capex and PPP coverage of Africa in 2026
curl -H "Authorization: Bearer mbg_live_..." \
  "/v1/search?q=capex&region=Africa&year=2026&terms=infrastructure-capex,ppp-finance&limit=50"

# 4. Ongoing projects in Kenya
curl -H "Authorization: Bearer mbg_live_..." \
  "/v1/projects?country=KE&status=ongoing&limit=100"

# 5. Everything Momberger has written about Schiphol
curl -H "Authorization: Bearer mbg_live_..." /v1/airports/AMS/articles

# 6. Industry events still to come
curl -H "Authorization: Bearer mbg_live_..." "/v1/calendar?upcoming=true"

MCP server

Not available yet

There is no MCP endpoint on this API today. mcp is a real entitlement and appears in GET /v1/me, but nothing serves the protocol behind it — so rather than print a configuration snippet that would fail to connect, this section says what the server will be when it lands.

Two tools, the whole API

The design is settled even though the code is not. One tool per endpoint would spend most of a model's context on descriptions, so the server will expose two and let the agent compose calls itself:

  • search_api(query) — find operations in the catalogue with their parameters and examples.
  • call_api(method, path, …) — invoke any /v1/* operation. Authentication and the entitlement gates apply exactly as they do over HTTP.

In the meantime

Every capable agent already knows how to make an HTTP request. Give it the spec block above and an API key and it can work the whole API today, without a protocol in between.

The OpenAPI document itself is at /openapi.json, which most agent frameworks can ingest directly.

API access is included in Professional

Ready to integrate?

Start a subscription, then mint a key from your account.