Agraris data is available as a static JSON file and through Supabase's auto-generated REST API (PostgREST). This page documents read queries (GET). To publish an agent, use the form at publish.html.
New to building agents? Read the guide.
The simplest way to read the registry. A single static JSON file
holds every published listing, agents and tools alike, newest
first. No key is needed. The file is regenerated automatically
once a day, and only changes when a listing does, so
generated_at stays the same until then. For custom
queries or fresher data, use the REST API below.
curl "https://agraris.xyz/api/agents.json"
Shape of the file:
{
"schema_version": 1,
"generated_at": "2026-10-16T00:00:00.000Z",
"count": 2,
"listings": [
{
"id": "…",
"kind": "tool",
"name": "…",
…
"url": "https://agraris.xyz/agent.html?id=…"
}
]
}
Each entry in listings has exactly these fields.
Fields may be added in later versions; a change that removes or
renames a field raises schema_version.
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | Listing id |
kind | string | agent or tool |
name | string | Listing name |
description | string | Short summary |
version | string | e.g. "1.0.0" |
repo_url | string | Link to source repo |
category | string[] | Category / tool tags |
demo_url | string | null | Optional demo link |
contract_address | string | null | Optional address provided by the builder, not verified by Agraris |
created_at | string (ISO 8601) | When the listing was submitted |
approved_at | string | null | When it was approved; null for listings from before the review queue |
github_stars | integer | null | Refreshed daily from GitHub |
github_updated_at | string | null | Last push to the repo |
verified | boolean | Marked "Verified" by Agraris |
live_status | string | null | up, down or null; see Live check |
live_checked_at | string | null | When the demo link was last checked |
wanted_id | string (uuid) | null | The Wanted request this listing answers |
owner_signed | boolean | true while the contract's owner() wallet has a current signature; see Contract owner signature |
owner_signed_at | string | null | When that signature was received |
builder_github | string | null | Lowercase GitHub owner from repo_url; null when the repo is not on github.com. See Builders |
url | string | The listing's page on agraris.xyz |
Listings are sorted by when they appeared in the registry
(approved_at, or created_at when that is
null), newest first. Only approved listings are included. To keep
only tools (or only agents), filter on kind, for
example with jq:
curl -s "https://agraris.xyz/api/agents.json" | jq '.listings[] | select(.kind=="tool")'
# Agents only
curl -s "https://agraris.xyz/api/agents.json" | jq '.listings[] | select(.kind=="agent")'
A short overview for language models is at llms.txt. Being listed is not an endorsement by Agraris.
All agent data lives behind a single endpoint — the agents table —
exposed by Supabase's built-in PostgREST.
https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents
Only the agents table is documented here. Other tables
in the Agraris database are not publicly readable.
Every request must include Agraris's Supabase publishable key. Requests without a key are rejected. All examples on this page already use this key, so you can copy and paste them as-is:
sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm
This is a publishable key, designed to be used in browsers. It only grants what the database's access rules allow. Please don't hammer the API; cache results on your side.
There are two ways to send the key — pick one. Via the apikey header (recommended):
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=*" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
Or via the ?apikey= query string — handy for opening
directly in a browser:
https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=*&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm
The fetch version in JavaScript:
const res = await fetch(
"https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=*",
{ headers: { apikey: "sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm" } }
);
const agents = await res.json();
Array filters use curly braces, e.g. cs.{trading}.
curl treats {} as a glob pattern, so URLs like this need
one of the following: wrap the URL in quotes and
pass the -g flag (--globoff), or encode the
braces as %7B and %7D
(cs.%7Btrading%7D). Always quote the URL so the shell
doesn't interpret &, *, and
{}. Browsers and fetch encode curly braces
automatically.
Fetch every agent in the registry, with all columns. Only approved agents are returned; see Review queue.
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=*" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=*&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
Filter by id (uuid) to fetch a single agent.
Replace AGENT_ID with the id of the agent you want.
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?id=eq.AGENT_ID&select=*" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
The category column is an array (text[]), so
?category=eq.trading won't match. Use the
cs (contains) operator to find agents with a given
tag.
curl -g "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?category=cs.{trading}" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version, braces encoded (no -g)
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?category=cs.%7Btrading%7D&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
For regular columns, use eq (equals). For example, only
agents marked Verified:
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?verified=eq.true" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?verified=eq.true&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
Other PostgREST operators such as neq, gt,
lt, ilike, and is work too —
for example ?name=ilike.*bot* for a case-insensitive
name search.
The registry lists two kinds of entries in the same table. The
kind column is agent or tool
(tools, MCP servers, APIs, SDKs and similar). A request without a
kind filter returns both, so add one if you only want
agents:
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?kind=eq.agent" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# only tools
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?kind=eq.tool" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
contract_address is optional and empty for most agents.
Use is to list only agents that have one:
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?contract_address=not.is.null&select=id,name,contract_address" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
To look up a specific address, use ilike so the match
ignores upper/lower case (builders may enter checksummed or
lowercase addresses):
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?contract_address=ilike.0x467b7a85f55bd716cb16d59f63b8ccac08852a6c" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
The address is provided by the builder. Agraris does not verify ownership. The Agraris API itself returns only the stored address, never on-chain data.
When an agent has a contract_address, its page shows an
"On-chain activity" section. The visitor's browser reads it directly
from the public Blockscout API for Robinhood Chain; it does not go
through Agraris or Supabase:
https://robinhoodchain.blockscout.com/api/v2/addresses/{address}
https://robinhoodchain.blockscout.com/api/v2/addresses/{address}/counters
| Shown as | Blockscout field | Notes |
|---|---|---|
| Address type | is_contract | "Contract" or "Not a contract" |
| Contract name | name | Shown only when it is a non-empty string |
| Source verified | is_verified | "Yes" or "No"; shown only when it is a boolean |
| Transactions | transactions_count | From /counters; a string such as "1164" |
| Token transfers | token_transfers_count | From /counters; a string |
The response's hash is compared with the stored address
without regard to upper/lower case (Blockscout returns the
checksummed form). The numbers are shown as Blockscout reports them,
without interpretation. A missing or unexpected field hides only its
own row. Neither endpoint returns a last-activity
date, so none is shown. If the request fails, is blocked, or takes
longer than 6 seconds, the section is hidden and the rest of the page
is unaffected.
Use select to fetch only specific columns,
comma-separated. Responses get smaller and faster.
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=name,category,github_stars" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=name,category,github_stars&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
Use order=column.asc or order=column.desc.
For example, sort by most GitHub stars (add
.nullslast so agents without star data yet
go at the end):
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?order=github_stars.desc.nullslast" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?order=github_stars.desc.nullslast&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
Use limit to cap the number of rows, and
offset for the next page.
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?limit=10" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?limit=10&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# second page
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?limit=10&offset=10" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
All of the parameters above can be combined in a single request with
&. Example: the 5 trading agents with the most
GitHub stars, with only the columns you need.
curl -g "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=name,category,github_stars&category=cs.{trading}&order=github_stars.desc.nullslast&limit=5" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
# query string version, braces encoded (no -g)
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?select=name,category,github_stars&category=cs.%7Btrading%7D&order=github_stars.desc.nullslast&limit=5&apikey=sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
The same request with fetch:
const params = new URLSearchParams({
select: "name,category,github_stars",
category: "cs.{trading}",
order: "github_stars.desc.nullslast",
limit: "5",
});
const res = await fetch(
`https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?${params}`,
{ headers: { apikey: "sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm" } }
);
const topTradingAgents = await res.json();
Each row in the agents table has the following columns.
| Column | Type | Notes |
|---|---|---|
id | uuid | Primary key |
name | text | Agent name |
description | text | Short summary of the agent |
version | text | e.g. "1.0.0" |
repo_url | text | Link to source repo |
category | text[] | Array of category / tool tags |
demo_url | text | Nullable |
created_at | timestamptz | When the agent was published |
verified | boolean | Marked "Verified" by Agraris |
github_stars | integer | Nullable; refreshed daily from GitHub |
github_updated_at | timestamptz | Nullable; last push to the repo |
contract_address | text | Optional; contract or wallet address (0x + 40 hex) provided by the builder, not verified by Agraris |
kind | text | agent or tool; filter with ?kind=eq.tool |
live_status | text | Nullable; up or down, see Live check |
live_checked_at | timestamptz | Nullable; when the demo link was last checked |
live_fail_count | integer | Failed checks in a row (0 after a success) |
wanted_id | uuid | Nullable; the Wanted request this listing answers. Set only when the listing is submitted |
approved | boolean | Always true in API responses; pending entries are not returned |
Every 6 hours Agraris requests the demo_url of each
published listing that has one (a plain GET over
https, with the User-Agent
AgrarisLiveCheck/1.0 (+https://agraris.xyz)). An HTTP
2xx or 3xx answer sets live_status to
up. A failure only counts up
live_fail_count; live_status becomes
down after 3 failed checks in a row, and one success
sets it back to up. live_status is null
until a listing's demo link has been checked, and is reset when
the demo link changes. Listings without a demo link are never
checked and show nothing.
This only means the demo URL responded. Not an endorsement: it
says nothing about what the demo does or about the listing
itself. Nothing from the response is stored. On the site, the
search page's "Demo responding" filter keeps listings whose
live_status is up.
curl -s "https://agraris.xyz/api/agents.json" | jq '.listings[] | select(.live_status=="up")'
Listings whose builders edited them through "Edit listing" in the
last 30 days, latest edit first, one entry per listing. Only
approved listings are included, and only edits made after a
listing was published count. The function returns
id, name, kind and
last_edited_at, nothing about what was changed.
p_limit defaults to 5 and is capped at 20.
curl -X POST "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/rpc/get_recently_updated" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm" \
-H "Content-Type: application/json" \
-d '{"p_limit": 5}'
On wanted.html anyone can describe an
agent or tool they need, without an account. Requests are reviewed
before they appear, are listed for 60 days, and carry no contact
details. Builders answer by publishing a listing with
wanted_id set to the request's id ("I built this"
opens publish.html?wanted=<id>). Only requests
that are currently listed can be answered, and
wanted_id cannot be changed after submitting.
get_wanted returns the listed requests, newest first:
id, title, description,
kind (agent, tool or
any), created_at and
answers_count (approved listings that answer it).
p_limit is capped at 50.
curl -X POST "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/rpc/get_wanted" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm" \
-H "Content-Type: application/json" \
-d '{"p_limit": 50}'
# Listings that answer one request
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/agents?wanted_id=eq.REQUEST_ID&select=id,name,kind" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
New requests go through submit_wanted
(p_title up to 100 characters,
p_description up to 500, p_kind). Links
and web addresses are rejected, and new requests are paused while
20 are waiting for review.
A builder whose listing has a contract address can show that the
wallet returned by the contract's owner() signed a
message. This starts from the listing's status link on
status.html, with a browser wallet
(personal_sign, EIP-191). The message is made by the
database and names agraris.xyz, the listing id, the
contract address, chain ID 4663, a one-time nonce and an expiry 10
minutes ahead.
The signature is checked on our server, not in the browser: a
GitHub Actions workflow recovers the signing address and reads
owner() from Robinhood Chain (chain ID 4663). The
listing gets the label "Contract owner signed a message" only when
both match and the owner is a regular wallet. Contracts without
owner(), with a zero owner, or whose owner is another
contract (for example a multisig) can't be shown this way. The
owner is read again every 6 hours, and the label is removed when it
changes or when the listing's contract address changes.
Only means the owner wallet signed a message. Not an endorsement.
It is separate from the manual verified flag.
Current signatures are readable in owner_proofs
(agent_id, signer, signed_at;
approved listings only), and as owner_signed /
owner_signed_at in the JSON file.
curl "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/owner_proofs?select=agent_id,signer,signed_at" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm"
curl -s "https://agraris.xyz/api/agents.json" | jq '.listings[] | select(.owner_signed)'
builders.html lists every GitHub account
that has at least one approved listing, in alphabetical order, and
builder.html?u=<owner> shows that account's
listings. The account is read from each listing's
repo_url: only https://github.com/<owner>/<repo>
(or www.github.com) counts, the owner is lowercased and
must match ^[a-z0-9-]{1,39}$. Other repo links have no
builder.
Grouped by the GitHub owner in each listing's repo URL. Anyone can list a repo, so this does not show who submitted it. Not an endorsement.
The same value is builder_github in the JSON file:
curl -s "https://agraris.xyz/api/agents.json" | jq '.listings[] | select(.builder_github=="OWNER")'
New agents submitted through the publish form are saved with
approved = false and wait for review before they
appear anywhere public. Until then they are left out of every API
response (including a lookup by id), the site, the
embed widget, the RSS feed, the sitemap and the weekly digest.
The public key can submit new agents but cannot approve them:
inserts with approved or verified set to
true are rejected, and updates are not allowed.
Because a pending row is not readable with the public key, a
submission must not ask for the inserted row back. With
supabase-js, call .insert() without
.select(); over REST, keep the default
Prefer: return=minimal.
To let builders follow their submission, the publish form sends a
random status code (status_token, a uuid made in the
browser) with the insert and links to
status.html. Only a hash of the code is
stored, so the column is no use to anyone reading the table. The
status page asks for the status with the
get_submission_status function, which returns only
status (pending or
approved), name and id, and
nothing when the code does not match.
curl -X POST "https://gnydurjxrynqcbpkxopd.supabase.co/rest/v1/rpc/get_submission_status" \
-H "apikey: sb_publishable_4roHAzTOTw9P0_8NXKt2IA_kXniR9pm" \
-H "Content-Type: application/json" \
-d '{"p_token": "STATUS_CODE"}'
When an agent is approved, approved_at records the
time. The RSS feed, the sitemap and the weekly digest date each
agent by when it appeared in the registry: approved_at,
or created_at for agents listed before the review
queue. The owner is emailed about new submissions and approves or
rejects them from a GitHub Actions workflow; a rejected submission
is deleted.
This API shares capacity with the Agraris site's own traffic, so there is no published quota. Please use it fairly — cache results on your side if you fetch repeatedly, and avoid aggressive polling.
Show an agent's card on your own site with an
<iframe>. The card shows the name, a two-line
description, category tags, GitHub stars, the Verified badge
(when the agent has it) and a "View on Agraris" link. Each agent
page has a ready-to-copy snippet under "Embed this agent".
| Parameter | Values | Notes |
|---|---|---|
id | agent uuid | Required. The same id as in agent.html?id=. |
theme | light | dark | Optional, defaults to light. The card's page background is transparent. |
<iframe id="agraris-embed"
src="https://agraris.xyz/embed.html?id=AGENT_ID&theme=dark"
width="420" height="180" style="border:0" loading="lazy"></iframe>
The card reports its height to the parent page with
postMessage, so you can resize the iframe to fit:
window.addEventListener("message", (e) => {
if (e.origin !== "https://agraris.xyz") return;
if (e.data?.type !== "agraris-embed-height") return;
document.querySelector("#agraris-embed").height = e.data.height;
});
GitHub READMEs don't support iframes. For a README, use the "Listed on Agraris" badge from the agent page instead.