Use InterfaceDelta in your enrichment pipeline
One Record API call per company domain tells you whether it runs agent-facing services, which protocols, when they last changed, and whether any change was breaking.
Many teams now replace boxed enrichment tools with their own scripts. The Record API’s domain lookup is a ready-made enrichment source for that pipeline: it is a closed projection of InterfaceDelta’s independently observed record, served as flat JSON with an evidence class per field.
What the lookup returns
GET /api/v1/record/lookup?domain=example.com accepts a domain or an HTTP(S) URL. It normalizes the host and matches that host and its subdomains. The response carries a summary for the whole domain plus per-service services entries, at most 10, sorted by atlas key. Every summary field has a sibling entry in an evidence map naming its evidence class.
- servicesFound, atlasUrl
- How many recorded services matched, and the public atlas page of the first one.
- hasMcp, hasServerCard, hasOpenApi, hasA2a, hasArd, hasWebMcp
- Whether any matched service currently exposes that agent-facing protocol, from the latest observation. Commerce flags
hasUcpandhasAp2appear only when commerce display is enabled. - asksForPayment
- An MCP endpoint answered Payment Required. It exposes no payment terms.
- toolsCount
- Tools read across the matched services’ latest observations, or null if any matched service has an unknown count.
- firstObservedAt, lastObservedAt
- The earliest and latest retained observations. firstObservedAt is not a claim about when a service launched.
- lastChangedAt, lastChangeClass
- The most recent retained comparable change and its class, for example
breakingornon_breaking. - breakingChanges30d, changes90d
- Retained changes in those windows. Dates and change counts cover retained history only;
boundedHistorystays true.
A domain with no recorded services is not an error: the lookup answers 200 with found: false, a zeroed summary and an empty services list, and still counts toward quota.
Authentication and the free quota
Create an API token in Account → API tokens with the record:read scope and send it as a Bearer token. The free plan includes 1,000 accepted Record API requests per month, shared by all tokens on the account and resetting at midnight UTC on the first day of the next month. Each token may send 60 requests per minute; a burst rejection does not consume monthly quota. Authentication and invalid-query errors do not count; every successfully constructed response does, including lookups with no matches.
curl -H "Authorization: Bearer $INTERFACEDELTA_API_TOKEN" \
"https://interfacedelta.com/api/v1/record/lookup?domain=example.com"Python waterfall
Look up each domain in turn; a miss marks the row not_observed and the loop continues; observed rows with breaking changes are flagged for review.
import json
import os
import urllib.error
import urllib.parse
import urllib.request
API = "https://interfacedelta.com/api/v1/record/lookup"
TOKEN = os.environ["INTERFACEDELTA_API_TOKEN"]
def lookup(domain):
url = f"{API}?domain={urllib.parse.quote(domain)}"
request = urllib.request.Request(
url, headers={"Authorization": f"Bearer {TOKEN}"}
)
with urllib.request.urlopen(request) as response:
return json.load(response)
def enrich(domains):
for domain in domains:
try:
record = lookup(domain)
except urllib.error.HTTPError as error:
if error.code == 429:
# Wait for Retry-After, or flag the row and continue.
yield {"domain": domain, "status": "rate_limited", "review": True}
continue
raise
if not record["found"]:
# Not in the record is not a clean bill of health; keep going.
yield {"domain": domain, "status": "not_observed"}
continue
summary = record["summary"]
yield {
"domain": domain,
"status": "observed",
"servicesFound": summary["servicesFound"],
"hasMcp": summary["hasMcp"],
"hasOpenApi": summary["hasOpenApi"],
"hasA2a": summary["hasA2a"],
"toolsCount": summary["toolsCount"],
"asksForPayment": summary["asksForPayment"],
"lastObservedAt": summary["lastObservedAt"],
"lastChangedAt": summary["lastChangedAt"],
"breakingChanges30d": summary["breakingChanges30d"],
"atlasUrl": summary["atlasUrl"],
"review": summary["breakingChanges30d"] > 0
or summary["lastChangeClass"] == "breaking",
}
for row in enrich(["example.com", "example.org"]):
print(json.dumps(row))JavaScript waterfall
The same waterfall in Node 18 or later, with the built-in fetch.
const API = "https://interfacedelta.com/api/v1/record/lookup";
async function lookup(domain, token) {
const url = `${API}?domain=${encodeURIComponent(domain)}`;
const response = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
});
if (response.status === 429) return { rateLimited: true };
if (!response.ok) throw new Error(`lookup failed for ${domain}`);
return { rateLimited: false, record: await response.json() };
}
async function enrich(domains, token) {
const rows = [];
for (const domain of domains) {
const { rateLimited, record } = await lookup(domain, token);
if (rateLimited) {
// Honour Retry-After, or flag the row and continue.
rows.push({ domain, status: "rate_limited", review: true });
continue;
}
if (!record.found) {
// Not in the record is not a clean bill of health; keep going.
rows.push({ domain, status: "not_observed" });
continue;
}
const summary = record.summary;
rows.push({
domain,
status: "observed",
servicesFound: summary.servicesFound,
hasMcp: summary.hasMcp,
hasOpenApi: summary.hasOpenApi,
hasA2a: summary.hasA2a,
toolsCount: summary.toolsCount,
asksForPayment: summary.asksForPayment,
lastObservedAt: summary.lastObservedAt,
lastChangedAt: summary.lastChangedAt,
breakingChanges30d: summary.breakingChanges30d,
atlasUrl: summary.atlasUrl,
review:
summary.breakingChanges30d > 0 ||
summary.lastChangeClass === "breaking",
});
}
return rows;
}
const token = process.env.INTERFACEDELTA_API_TOKEN;
console.log(await enrich(["example.com", "example.org"], token));Sample response
Computed by the same code path as the live lookup, over the fixtures used by the record tests: one service on example.com with a retained breaking change the day before the snapshot. Commerce fields are omitted, as they are when commerce display is off.
Show all 136 lines
{
"recordVersion": "record-v1",
"generatedAt": "2026-11-02T00:00:00.000Z",
"coverage": {
"boundedHistory": true,
"truncated": false
},
"provenance": {
"manifestVersion": "provenance-v1",
"publisher": "InterfaceDelta, Inc.",
"collection": {
"agent": "InterfaceDeltaBot/1.0 (+https://interfacedelta.com/bot)",
"policy": "https://interfacedelta.com/bot",
"method": "Passive observation of public agent interfaces: robots.txt honoured, no credentials sent, no tools called, nothing paid or signed."
},
"sources": [
{
"id": "official_registry",
"name": "Official MCP Registry",
"role": "listing",
"licence": "CC0-1.0",
"url": "https://registry.modelcontextprotocol.io"
}
],
"listingsAreLeads": "Listings only point InterfaceDelta at a service; every recorded fact comes from InterfaceDelta’s own observation of that service.",
"evidenceClasses": [
{
"id": "DIRECTLY_OBSERVED",
"meaning": "Recorded by InterfaceDelta from the service’s own public response."
},
{
"id": "PUBLISHER_DECLARED",
"meaning": "Stated by the publisher in its own interface or listing; not verified."
},
{
"id": "INTERFACEDELTA_DERIVED",
"meaning": "Computed by InterfaceDelta with fixed rules from recorded evidence."
},
{
"id": "INTERFACEDELTA_INFERRED",
"meaning": "Estimated by InterfaceDelta, including AI readings; labelled and checked against recorded evidence."
}
],
"rules": {
"changeClassification": "compatibility-rules-v12"
},
"snapshot": {
"generatedAt": "2026-11-02T00:00:00.000Z",
"truncated": false
},
"noVerdicts": "Facts and evidence only. InterfaceDelta assigns no safety, trust or risk scores."
},
"found": true,
"summary": {
"domain": "example.com",
"servicesFound": 1,
"atlasUrl": "https://interfacedelta.com/atlas/sha256%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"hasMcp": true,
"hasA2a": false,
"hasOpenApi": false,
"hasWebMcp": false,
"hasArd": false,
"hasServerCard": false,
"asksForPayment": false,
"toolsCount": 3,
"firstObservedAt": "2026-11-01T00:00:00.000Z",
"lastObservedAt": "2026-11-02T00:00:00.000Z",
"lastChangedAt": "2026-11-01T00:00:00.000Z",
"lastChangeClass": "breaking",
"breakingChanges30d": 1,
"changes90d": 1,
"boundedHistory": true,
"evidence": {
"domain": "INTERFACEDELTA_DERIVED",
"servicesFound": "INTERFACEDELTA_DERIVED",
"atlasUrl": "INTERFACEDELTA_DERIVED",
"hasMcp": "DIRECTLY_OBSERVED",
"hasA2a": "DIRECTLY_OBSERVED",
"hasOpenApi": "DIRECTLY_OBSERVED",
"hasWebMcp": "DIRECTLY_OBSERVED",
"hasArd": "DIRECTLY_OBSERVED",
"hasServerCard": "DIRECTLY_OBSERVED",
"asksForPayment": "INTERFACEDELTA_DERIVED",
"toolsCount": "INTERFACEDELTA_DERIVED",
"firstObservedAt": "DIRECTLY_OBSERVED",
"lastObservedAt": "DIRECTLY_OBSERVED",
"lastChangedAt": "INTERFACEDELTA_DERIVED",
"lastChangeClass": "INTERFACEDELTA_DERIVED",
"breakingChanges30d": "INTERFACEDELTA_DERIVED",
"changes90d": "INTERFACEDELTA_DERIVED",
"boundedHistory": "INTERFACEDELTA_DERIVED"
}
},
"services": [
{
"domain": "example.com",
"servicesFound": 1,
"atlasUrl": "https://interfacedelta.com/atlas/sha256%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"hasMcp": true,
"hasA2a": false,
"hasOpenApi": false,
"hasWebMcp": false,
"hasArd": false,
"hasServerCard": false,
"asksForPayment": false,
"toolsCount": 3,
"firstObservedAt": "2026-11-01T00:00:00.000Z",
"lastObservedAt": "2026-11-02T00:00:00.000Z",
"lastChangedAt": "2026-11-01T00:00:00.000Z",
"lastChangeClass": "breaking",
"breakingChanges30d": 1,
"changes90d": 1,
"boundedHistory": true,
"evidence": {
"domain": "INTERFACEDELTA_DERIVED",
"servicesFound": "INTERFACEDELTA_DERIVED",
"atlasUrl": "INTERFACEDELTA_DERIVED",
"hasMcp": "DIRECTLY_OBSERVED",
"hasA2a": "DIRECTLY_OBSERVED",
"hasOpenApi": "DIRECTLY_OBSERVED",
"hasWebMcp": "DIRECTLY_OBSERVED",
"hasArd": "DIRECTLY_OBSERVED",
"hasServerCard": "DIRECTLY_OBSERVED",
"asksForPayment": "INTERFACEDELTA_DERIVED",
"toolsCount": "INTERFACEDELTA_DERIVED",
"firstObservedAt": "DIRECTLY_OBSERVED",
"lastObservedAt": "DIRECTLY_OBSERVED",
"lastChangedAt": "INTERFACEDELTA_DERIVED",
"lastChangeClass": "INTERFACEDELTA_DERIVED",
"breakingChanges30d": "INTERFACEDELTA_DERIVED",
"changes90d": "INTERFACEDELTA_DERIVED",
"boundedHistory": "INTERFACEDELTA_DERIVED"
}
}
]
}Reading the fields
Evidence classes are DIRECTLY_OBSERVED (recorded from the service’s own public response), PUBLISHER_DECLARED, INTERFACEDELTA_DERIVED and INTERFACEDELTA_INFERRED. In the lookup, a summary field whose evidence is DIRECTLY_OBSERVED was read from a single matched service; aggregated fields are derived. Every response also carries provenance: who collected the data, each listing source and licence, and the rule version that classified changes.
This is a bounded snapshot, not a durable archive: dates and change counts cover retained history only, and a null lastChangedAt does not establish that nothing ever changed. The full field list, including the per-service shape, is in the Record API OpenAPI document and the Record API reference.
Paid volumes and licensing
Higher volumes, and any licence to redistribute or republish record data inside a product, are by agreement; the included monthly quota is the one shown on pricing. InterfaceDelta publishes facts and evidence classes, never verdicts: keep it that way in your pipeline’s output.

