FreeAPI.watch

Shipping an MCP Server So Your Agent Stops Recommending Dead APIs

 ·  engineering

A no-auth MCP server at api.freeapi.watch/mcp, 30 new APIs in the catalog, and three bugs the expansion dragged into the light — including a ranking bug that put brand-new working APIs below one with 9% uptime.

FreeAPI.watch has an MCP server as of this week. It lives at https://api.freeapi.watch/mcp, needs no key and no signup, and gives any AI assistant six tools for finding out whether a free public API is alive right now — as opposed to whether it was alive when the model was trained.

The catalog grew from 113 entries to 143 in the same week. Adding 30 rows in one morning turned out to be the most useful thing I did, because it exposed a ranking bug that had been sitting quietly in the page templates for months. That part gets more room than the announcement.

Why an Agent Needs a Live Status Feed

Ask an AI assistant for a free weather API and there is a real chance it suggests Dark Sky. Apple bought Dark Sky and shut the API down in 2023. Ask for a free news API and you may get Twitter v1.1, whose free access was killed in July 2023. Both answers were correct once, which is exactly the problem.

This is a data freshness problem, not a model quality problem. Models are trained on documentation, tutorials and blog posts, and that corpus ages badly in one specific direction: an API that was free and healthy when the training data was collected may today be dead, paywalled or key-gated. Nothing in the text announces the change. The tutorial that recommended it still reads as confidently as the day it was written, and the model has no way to tell a live fact from a fossilised one.

The correction has to come from outside the weights. We already ping every API in the catalog hourly with a real HTTP request against a real endpoint; the MCP server exposes that measurement to an agent at the exact moment it is about to write an integration. It is the difference between recalling what was true and checking what is true. Our /graveyard page is the human-readable version of the same data — this is the machine-readable one.

The Six Tools

Every tool answers from the same live monitoring database that the site renders from:

  • check_api_status — up or down right now for one API, with measured response time, 30-day uptime, free-tier terms and whether a key is required.
  • search_free_apis — APIs matching a topic, ranked by measured uptime, filterable to no-key-required or a minimum uptime threshold.
  • find_alternatives — given an API that died, went paid, or is rate-limiting you, free replacements in the same category ranked by uptime.
  • list_dead_apis — the graveyard, each entry paired with what still works instead of it.
  • check_mcp_servers — live status of the public no-auth remote MCP servers we track, each probed hourly with a real JSON-RPC initialize handshake.
  • list_categories — every category in the catalog with counts and how many are up right now.

Adding it to Claude Code is one command:

claude mcp add freeapi-watch --transport http https://api.freeapi.watch/mcp

There is nothing else to configure — no key to paste, no OAuth round trip, no local process to keep running. If you want to see the wire format, or you are calling it from a language with no MCP SDK, the endpoint is a plain HTTP POST:

curl -X POST https://api.freeapi.watch/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"check_api_status",
                 "arguments":{"api":"open-meteo"}}}'

The transport is Streamable HTTP against MCP specification revision 2025-06-18. It responds with JSON rather than opening an SSE stream, because every tool resolves in a single database round-trip — there is no partial output to stream, and holding a connection open would add latency without buying anything. Full tool schemas, per-client config snippets and the rate limits are on /mcp-server.

It Runs on the Worker That Was Already There

No new infrastructure. The MCP endpoint is another route on the same Cloudflare Worker that already serves the site's snapshot JSON, reading the same D1 database the hourly pinger writes into. Still $0/month on the free tier.

We also added our own MCP server to our own monitored catalog. It gets probed hourly with the same JSON-RPC initialize handshake as every other remote server listed on /free-mcp-servers. Monitoring your own status endpoint sounds like a stunt right up until the morning it catches you. If ours breaks, our own site says so, and I would rather learn that from the same table everyone else reads than from an email.

113 to 143 Entries, 8 Categories to 14

Six new categories came with the expansion: entertainment, music, sports, games, food and books. The notable additions are TMDB, OMDb and TVmaze for film and television, Jikan for anime, xkcd for comics, MusicBrainz, iTunes Search and Deezer for music, TheSportsDB and football-data.org for sports, Scryfall and Deck of Cards for games, Open Food Facts for food, Google Books and Datamuse for text, USGS Earthquakes and Wikidata for public data, and DummyJSON for the perennial need for plausible fake data in a demo.

Every single one was verified with a real request before it was added. That is the entire premise of the project — seeding a catalog from someone else's markdown file just launders stale data into a new format. Three candidates failed the check and were dropped rather than shipped with an asterisk: the SpaceX API returns HTTP 525, meaning the origin TLS handshake is failing behind Cloudflare; Advice Slip times out on connection; and Marvel discontinued its developer portal outright.

Two Failures Local Testing Could Not Have Caught

This is the part worth the read. Two of the new APIs passed a manual check from my machine and failed from production, for entirely different reasons, and both would have shipped as false data if the monitoring lived on a laptop.

iTunes Search returned 200 from a residential IP and 403 from the Cloudflare Worker. Apple blocks datacentre IP ranges on the iTunes Search API. The service is demonstrably up — it answers in 88ms — it just refuses our colo. Recording that as dead would be a lie, and dropping the API would punish users for where we happen to run. So 403 is now the expected status for itunes-search, the same treatment already applied to reddit-json, weatherbit and binance-public. The hourly check still runs, and it still fails loudly the moment the response stops being a 403. How expected-status handling works is written up on /methodology.

The general lesson is the one every monitoring system eventually teaches: where you test from changes what you measure. A green check from your laptop tells you the API works for your laptop. If your production traffic leaves a cloud provider's IP range, that is the only vantage point whose answer counts.

Scryfall was subtler and more embarrassing. 200 locally, 400 from the Worker. The health URL carried a query string — `?exact=Black+Lotus` — and the `+` survived Workers' URL normalisation differently than it did in my shell, so the card lookup missed and Scryfall correctly returned 400 for a card it could not find. Nothing was wrong with Scryfall. We were testing a URL encoding edge case by accident and calling the result an uptime measurement.

The fix was to remove the variable rather than encode around it: the health URL is now `/symbology`, which takes no parameters at all. A health check should exercise the smallest thing that proves the service is answering, not the most interesting thing you can think of to ask it. Every parameter in a health URL is one more thing that can fail for reasons that have nothing to do with the service being up.

The Ranking Bug That 29 New Rows Exposed

Daily uptime rollups are computed at 02:00 UTC. An API added at 09:00 therefore has no history at all until the following morning — a known and unavoidable gap in a system that measures things over time.

The page templates treated no history as 0% uptime. The result, on the morning after the expansion: all 29 APIs added that day ranked below APIs that were measurably broken. A brand-new API answering every request sat underneath one with 9% uptime, on pages whose entire purpose is telling you which APIs work. The absence of evidence was being rendered as evidence of failure.

Pulling on that thread surfaced two more problems that had been latent for months. First, ranking read only the most recent day — `rollups.at(-1)` — while the detail pages displayed a 30-day mean. Two different numbers were being presented under the same word, uptime, and nobody noticed for as long as the two happened to stay close. Second, the no-history fallback was hard-coded as 0 in three templates and 50 in four, so the number you saw depended on which page you landed on.

All of it collapsed into a single shared helper. Rank on the 30-day mean, so the ranked number and the displayed number are the same number. Give an API that is answering but unproven a provisional 90 — below any established API with a genuinely good record, above anything that is actually failing. An API whose only check failed scores 0. One that has never been checked at all sits at 50.

Honest framing: this is the kind of bug you only find by adding 29 rows at once. At the usual pace of two or three additions a week, the new entries were buried far enough down each page that nobody — me very much included — ever looked at where they landed.

284 Sequential Round-Trips

The public JSON API had started returning 503s, which I noticed because the same snapshot endpoint feeds the static build. The cause was straightforward once measured rather than guessed: the endpoint looped two database queries per API. At 142 APIs that is 284 sequential round-trips, and the whole request took 5.6 seconds — comfortably past the point where things start timing out under any concurrency at all.

It is now two bulk queries, one for latest checks and one for rollups, joined in memory. The endpoint answers in roughly 800ms with no errors. The N+1 query is the oldest performance bug there is, and it still finds you, because the loop gets written when there are 40 rows and nobody revisits it at 142.

What I Think Actually Matters Here

A directory a human reads is useful once, at the moment they are choosing. A directory an agent can query is useful every single time it writes an integration — and the failure it prevents, an assistant confidently generating code against an API that shut down two years ago, is one people are hitting constantly right now without recognising the shape of it.

If you use Claude Code, Claude Desktop, Cursor, or anything else that speaks MCP, the endpoint is https://api.freeapi.watch/mcp and the documentation is on /mcp-server. If it ever stops working, the site will tell you before I do.

Frequently Asked Questions

How do I check if an API is still working?

Send a real request to the endpoint you intend to use and look at the status code — documentation and blog posts lag reality by months. If we already monitor the API, the FreeAPI.watch detail page shows the result of an hourly check against a real endpoint plus 30-day uptime, so you don't have to run the test yourself. From an AI assistant, the MCP server at https://api.freeapi.watch/mcp answers the same question with the check_api_status tool.

What is an MCP server?

MCP (Model Context Protocol) is a standard way to give an AI assistant tools it can call during a conversation. An MCP server exposes a set of named tools with typed arguments; the assistant decides when to call them and gets structured data back. A remote MCP server like ours is just an HTTP endpoint — you point your client at a URL rather than installing anything locally.

Does the FreeAPI.watch MCP server need an API key?

No. There is no key, no signup, no OAuth flow and no credit card. Point any MCP client at https://api.freeapi.watch/mcp and it works immediately. Fair-use rate limiting applies, and the full terms are on /mcp-server.

Why do AI assistants recommend APIs that no longer exist?

Because they learned from documentation, tutorials and blog posts, and that corpus ages in one direction. An API that was free and healthy when the training data was collected may be dead, paywalled or key-gated today, and nothing in the text says so — the old tutorial reads exactly as confidently as it did the day it was published. The fix isn't a smarter model, it's a live source the model can query at the moment it matters.

How do I add the FreeAPI.watch MCP server to Claude Code?

Run: claude mcp add freeapi-watch --transport http https://api.freeapi.watch/mcp — that is the whole setup. For Claude Desktop, Cursor or any client that uses a JSON config, add an entry with the type http and that URL; ready-made snippets for each client are on /mcp-server.

← All articles