State of Agent-to-Agent: Q3 2026
How many of the top one million websites publish an A2A Agent Card, and what those cards declare.
Key figures
- Domains checked
- 999,550
- of 1,000,000 in the list
- Served an Agent Card
- 274
- 0.027% of domains checked
- Use the v1.0 shape
- 200
- 73.0% of cards
- Signed
- 0
- 0% of cards
Adoption
Agent Cards by site popularity
Share of domains in each Tranco rank range that served an Agent Card
Show as table
| Tranco rank | Domains checked | Agent Cards | Share |
|---|---|---|---|
| 1 to 1,000 | 1,000 | 0 | 0% |
| 1,001 to 10,000 | 9,000 | 3 | 0.033% |
| 10,001 to 100,000 | 89,900 | 34 | 0.038% |
| 100,001 to 1,000,000 | 899,650 | 237 | 0.026% |
What each domain returned
Share of domains checked, by outcome
Show as table
| Outcome | 1 to 1,000 | 1,001 to 10,000 | 10,001 to 100,000 | 100,001 to 1,000,000 | All | Share |
|---|---|---|---|---|---|---|
| Agent Card, v1.0 shape | 0 | 2 | 19 | 179 | 200 | 0.020% |
| Agent Card, v0.3 shape | 0 | 1 | 15 | 58 | 74 | 0.007% |
| JSON, not an Agent Card | 4 | 13 | 114 | 1,056 | 1,187 | 0.12% |
| Not JSON | 90 | 766 | 8,072 | 77,836 | 86,764 | 8.7% |
| HTTP error status | 511 | 5,514 | 53,859 | 515,045 | 574,929 | 57.5% |
| Blocked by robots.txt | 111 | 812 | 10,650 | 164,283 | 175,856 | 17.6% |
| Timed out | 52 | 418 | 5,118 | 54,880 | 60,468 | 6.0% |
| Connection failed | 222 | 1,408 | 11,470 | 81,877 | 94,977 | 9.5% |
| Redirect not followed | 8 | 55 | 473 | 4,342 | 4,878 | 0.49% |
| Invalid domain name | 2 | 11 | 110 | 94 | 217 | 0.022% |
What the cards declare
Shares below are of the 274 cards found. They count what each card declares; the crawl did not call any agent.
Card shape and location
Share of cards, by shape and the path that served them
Show as table
| Shape and path | Cards | Share |
|---|---|---|
| v1.0 shape at agent-card.json | 199 | 72.6% |
| v0.3 shape at agent-card.json | 68 | 24.8% |
| v0.3 shape at agent.json | 6 | 2.2% |
| v1.0 shape at agent.json | 1 | 0.36% |
Declared capabilities
Share of cards that set each capability to true
Show as table
| Capability | Cards | Share |
|---|---|---|
| Streaming | 62 | 22.6% |
| Push notifications | 37 | 13.5% |
| At least one extension | 18 | 6.6% |
| Extended card | 0 | 0% |
Protocol bindings
Share of cards declaring each binding (a card can declare several)
Show as table
| Binding | Cards | Share |
|---|---|---|
| JSON-RPC (JSONRPC) | 88 | 32.1% |
| HTTP+JSON/REST (HTTP+JSON) | 102 | 37.2% |
| gRPC (GRPC) | 0 | 0% |
| Another value | 125 | 45.6% |
| None declared | 45 | 16.4% |
Authentication schemes
Share of cards declaring each security scheme type (a card can declare several)
Show as table
| Scheme type | Cards | Share |
|---|---|---|
| OAuth 2.0 | 57 | 20.8% |
| HTTP, other scheme | 30 | 10.9% |
| API key | 23 | 8.4% |
| HTTP Bearer | 16 | 5.8% |
| Other (non-standard type) | 5 | 1.8% |
| OpenID Connect | 2 | 0.73% |
| None declared | 194 | 70.8% |
Skills per card
Share of cards, by number of skills listed
Show as table
| Skills | Cards | Share |
|---|---|---|
| 0 skills | 5 | 1.8% |
| 1 skill | 38 | 13.9% |
| 2 to 4 skills | 131 | 47.8% |
| 5 to 9 skills | 94 | 34.3% |
| 10 to 19 skills | 4 | 1.5% |
| 20+ skills | 2 | 0.73% |
Signing and HTTP caching
Share of cards with each property
Show as table
| Property | Cards | Share |
|---|---|---|
| Signed (signatures present) | 0 | 0% |
| Content-Type: application/json | 270 | 98.5% |
| Cache-Control header | 232 | 84.7% |
| Cache-Control max-age above 0 | 161 | 58.8% |
| ETag header | 120 | 43.8% |
| max-age above 0 and ETag | 74 | 27.0% |
| application/a2a+json | 2 | 0.73% |
| other | 1 | 0.36% |
| text/html | 1 | 0.36% |
Provider organizations
- Cards naming a provider organization
- 175
- 63.9% of cards
- Distinct organization names
- 141
- Names on more than one domain
- 7
- covering 41 cards
- Distinct card documents
- 262
- by SHA-256 of the body
Counts only. This report does not name organizations or domains. Names are compared case-insensitively.
Why the other domains returned no card
999,276 domains returned no card. The detail recorded for each outcome, largest first:
| Outcome and detail | Domains | Share of checked |
|---|---|---|
| HTTP error status | 574,929 | 57.5% |
| 404 or 410 (not found) | 473,887 | 47.4% |
| 401 or 403 (refused) | 85,398 | 8.5% |
| Other 4xx status | 7,001 | 0.70% |
| 5xx (server error) | 6,830 | 0.68% |
| 429 (rate limited) | 935 | 0.094% |
| 3xx that could not be followed | 878 | 0.088% |
| Blocked by robots.txt | 175,856 | 17.6% |
| robots.txt returned 429 or 5xx (full disallow) | 106,470 | 10.7% |
| robots.txt rules disallow the paths | 53,067 | 5.3% |
| robots.txt redirect not followed | 16,319 | 1.6% |
| Connection failed | 94,977 | 9.5% |
| Host did not resolve (530) | 94,901 | 9.5% |
| Other network error | 76 | 0.008% |
| Not JSON | 86,764 | 8.7% |
| HTML instead of JSON | 71,810 | 7.2% |
| Body larger than 256 KiB | 12,957 | 1.3% |
| Other non-JSON body | 1,997 | 0.20% |
| Timed out | 60,468 | 6.0% |
| robots.txt took over 5 s | 56,201 | 5.6% |
| Card path took over 5 s | 4,267 | 0.43% |
| Redirect not followed | 4,878 | 0.49% |
| Redirect to a non-HTTPS URL | 3,520 | 0.35% |
| More than 3 redirects | 1,279 | 0.13% |
| Redirect to a blocked host or port | 79 | 0.008% |
| JSON, not an Agent Card | 1,187 | 0.12% |
| JSON without Agent Card fields | 1,187 | 0.12% |
| Invalid domain name | 217 | 0.022% |
| Invalid or reserved name | 217 | 0.022% |
Data and reproducibility
The aggregate CSV has every figure on this page, one row per figure, with its count and base. It has no per-domain rows and no copies of cards.
Every number comes from one export of the crawl database, taken September 27, 2026. The methodology explains what was requested and how each outcome is classified. These are the queries the export ran:
Show the 10 SQL queries
Crawl run: Tranco list, start and finish times, domains queued and checked, status.
SELECT id, list_id, source, started_at, finished_at, domains_total, domains_done, status FROM crawls WHERE id = 'full-20260926-39db43'Domains per rank bucket and outcome class (crawl_stats holds totals only, no per-domain rows).
SELECT bucket, classification, SUM(count) AS domains FROM crawl_stats WHERE crawl_id = 'full-20260926-39db43' GROUP BY bucket, classification ORDER BY bucket, classificationDomains without a card, grouped into reasons from the stored detail (HTTP status, robots.txt result, network error kind).
SELECT classification, CASE WHEN classification = 'http-error' AND detail IN ('404', '410') THEN 'not-found' WHEN classification = 'http-error' AND detail IN ('401', '403') THEN 'forbidden' WHEN classification = 'http-error' AND detail = '429' THEN 'rate-limited' WHEN classification = 'http-error' AND detail LIKE '5__' THEN 'server-error' WHEN classification = 'http-error' AND detail LIKE '3__' THEN 'redirect-status' WHEN classification = 'http-error' THEN 'other-status' WHEN classification = 'robots-disallowed' AND detail = 'rules' THEN 'robots-rules' WHEN classification = 'robots-disallowed' AND (detail = 'robots.txt 429' OR detail LIKE 'robots.txt 5__') THEN 'robots-unavailable' WHEN classification = 'robots-disallowed' THEN 'robots-redirect' WHEN classification = 'network-error' AND detail LIKE '%530' THEN 'unresolved' WHEN classification = 'network-error' AND detail LIKE '%dns' THEN 'dns' WHEN classification = 'network-error' AND detail LIKE '%tls' THEN 'tls' WHEN classification = 'network-error' AND (detail LIKE '%refused' OR detail LIKE '%reset') THEN 'connection' WHEN classification = 'network-error' THEN 'other-network' WHEN classification = 'timeout' AND detail = 'robots.txt' THEN 'robots-timeout' WHEN classification = 'timeout' THEN 'card-timeout' WHEN classification = 'not-json' AND detail = 'text/html' THEN 'html' WHEN classification = 'not-json' AND detail = 'oversize' THEN 'oversize' WHEN classification = 'not-json' THEN 'other-body' WHEN classification = 'redirect-error' AND detail = 'blocked-not-https' THEN 'redirect-not-https' WHEN classification = 'redirect-error' AND detail = 'too-many-redirects' THEN 'redirect-too-many' WHEN classification = 'redirect-error' THEN 'redirect-blocked' ELSE classification END AS reason, SUM(count) AS domains FROM crawl_stats WHERE crawl_id = 'full-20260926-39db43' AND classification NOT IN ('card-v1', 'card-v0.3') GROUP BY classification, reason ORDER BY domains DESC, classification, reasonCards by shape (1.0 = supportedInterfaces, 0.3 = url plus protocolVersion or preferredTransport) and by the path that served them.
SELECT version, path, COUNT(*) AS cards FROM cards WHERE crawl_id = 'full-20260926-39db43' GROUP BY version, path ORDER BY cards DESC, version, pathCard totals: distinct documents, signatures present, HTTP caching headers, declared capabilities, security schemes, provider organization present.
SELECT COUNT(*) AS cards, COUNT(DISTINCT raw_sha256) AS distinct_documents, COALESCE(SUM(signed), 0) AS signed, COALESCE(SUM(CASE WHEN cache_control IS NOT NULL AND trim(cache_control) <> '' THEN 1 ELSE 0 END), 0) AS cache_control_present, COALESCE(SUM(CASE WHEN (instr(lower(cache_control), 'max-age=') > 0 AND CAST(substr(lower(cache_control), instr(lower(cache_control), 'max-age=') + 8) AS INTEGER) > 0) THEN 1 ELSE 0 END), 0) AS max_age_positive, COALESCE(SUM(CASE WHEN lower(cache_control) LIKE '%no-store%' THEN 1 ELSE 0 END), 0) AS no_store, COALESCE(SUM(etag_present), 0) AS etag, COALESCE(SUM(CASE WHEN etag_present = 1 AND (instr(lower(cache_control), 'max-age=') > 0 AND CAST(substr(lower(cache_control), instr(lower(cache_control), 'max-age=') + 8) AS INTEGER) > 0) THEN 1 ELSE 0 END), 0) AS max_age_and_etag, COALESCE(SUM(CASE WHEN json_extract(capabilities_json, '$.streaming') = 1 THEN 1 ELSE 0 END), 0) AS streaming, COALESCE(SUM(CASE WHEN json_extract(capabilities_json, '$.pushNotifications') = 1 THEN 1 ELSE 0 END), 0) AS push_notifications, COALESCE(SUM(CASE WHEN json_array_length(capabilities_json, '$.extensions') > 0 THEN 1 ELSE 0 END), 0) AS extensions, COALESCE(SUM(CASE WHEN json_extract(capabilities_json, '$.extendedAgentCard') = 1 THEN 1 ELSE 0 END), 0) AS extended_agent_card, COALESCE(SUM(CASE WHEN json_array_length(security_schemes) = 0 THEN 1 ELSE 0 END), 0) AS no_security_schemes, COALESCE(SUM(CASE WHEN provider_org IS NOT NULL AND trim(provider_org) <> '' THEN 1 ELSE 0 END), 0) AS with_provider_org FROM cards WHERE crawl_id = 'full-20260926-39db43'Cards by response media type (Content-Type without parameters); uncommon values fold into 'other'.
SELECT CASE WHEN mt = '' THEN 'none' WHEN mt IN ('application/json', 'application/a2a+json', 'text/plain', 'text/html', 'application/octet-stream') THEN mt ELSE 'other' END AS media_type, COUNT(*) AS cards FROM (SELECT lower(trim(CASE WHEN instr(COALESCE(content_type, ''), ';') > 0 THEN substr(content_type, 1, instr(content_type, ';') - 1) ELSE COALESCE(content_type, '') END)) AS mt FROM cards WHERE crawl_id = 'full-20260926-39db43') GROUP BY media_type ORDER BY cards DESC, media_typeCards declaring each security scheme type (a card can declare several); non-standard types fold into 'other'.
SELECT CASE WHEN j.value IN ('apiKey', 'oauth2', 'openIdConnect', 'mutualTLS', 'http:bearer', 'http:basic') THEN j.value WHEN j.value LIKE 'http:%' THEN 'http:other' ELSE 'other' END AS scheme, COUNT(DISTINCT c.domain) AS cards FROM cards AS c, json_each(c.security_schemes) AS j WHERE c.crawl_id = 'full-20260926-39db43' GROUP BY scheme ORDER BY cards DESC, schemeCards by number of skills.
SELECT CASE WHEN skills_count = 0 THEN '0' WHEN skills_count = 1 THEN '1' WHEN skills_count <= 4 THEN '2-4' WHEN skills_count <= 9 THEN '5-9' WHEN skills_count <= 19 THEN '10-19' ELSE '20+' END AS skills, COUNT(*) AS cards FROM cards WHERE crawl_id = 'full-20260926-39db43' GROUP BY skillsProvider organizations as counts only: how many distinct names (case-insensitive), how many appear on more than one domain.
SELECT COUNT(*) AS organizations, COALESCE(SUM(CASE WHEN n > 1 THEN 1 ELSE 0 END), 0) AS organizations_on_several_domains, COALESCE(SUM(CASE WHEN n > 1 THEN n ELSE 0 END), 0) AS cards_from_those, COALESCE(MAX(n), 0) AS most_domains_for_one FROM (SELECT lower(trim(provider_org)) AS org, COUNT(*) AS n FROM cards WHERE crawl_id = 'full-20260926-39db43' AND provider_org IS NOT NULL AND trim(provider_org) <> '' GROUP BY org)Internal: the domains with a card and their shape, used only to read the stored raw cards in R2. Never exported.
SELECT domain, version FROM cards WHERE crawl_id = 'full-20260926-39db43' ORDER BY domain
Read from the stored raw cards (R2 emissar-crawl, key cards/full-20260926-39db43/<domain>.json (npx wrangler r2 object get ... --remote --pipe)):
- Protocol bindings: v1.0 cards, supportedInterfaces[].protocolBinding, or that entry's transport field (the v0.3 name) when protocolBinding is absent; v0.3 cards, preferredTransport and additionalInterfaces[].transport. Compared case-insensitively with JSONRPC, GRPC and HTTP+JSON; any other value counts as 'other'. A card counts once per binding.
- v1TransportField: v1.0 cards with at least one supportedInterfaces entry that has transport but no protocolBinding.
- Extended card: v1.0 cards with capabilities.extendedAgentCard = true; v0.3 cards with supportsAuthenticatedExtendedCard = true.
How to cite
Emissar team. "State of Agent-to-Agent: Q3 2026." Emissar, published September 27, 2026. https://emissar.ai/research/state-of-a2a/2026-q3. Data: crawl full-20260926-39db43 of Tranco list L5PZ4 (2026-09-25).Sources
- A2A protocol specification: Agent Card, discovery, caching and signatures
- Tranco list L5PZ4; Le Pochat et al., "Tranco: A Research-Oriented Top Sites Ranking Hardened Against Manipulation," NDSS 2019
- RFC 9309: Robots Exclusion Protocol