API documentation.
The FerroYard Public API serves a yard's published listings to any website or system — photos, specs, pricing and seller info, ready to render on a public inventory page.
Overview
- Base URL:
https://ferroyard.com— all endpoints under/api/v1/, JSON in and out. - Only listings that are ACTIVE and marked published to public site are exposed. Drafts, internal records and hidden photos never appear.
- The API is tenant-scoped by token: your token only ever returns your workspace's listings. There is no tenant parameter to get wrong.
- Read-only — tokens cannot create or modify anything.
Authentication
Generate a token in Settings → Workflow → Integrations(you'll need a FerroYard account with settings access). The full token — fy_live_… — is shown once; FerroYard stores only a hash. Send it on every request:
Authorization: Bearer fy_live_YOUR_TOKENCall the API from your server, not from visitors' browsers — a token in client-side JavaScript is public. Typical pattern:
// Next.js example — keep the token on YOUR server, never in browser code
export default async function InventoryPage() {
const res = await fetch('https://ferroyard.com/api/v1/inventory?limit=24', {
headers: { Authorization: `Bearer ${process.env.FERROYARD_API_TOKEN}` },
next: { revalidate: 300 }, // cache 5 minutes
});
const { data } = await res.json();
return <Listings items={data} />;
}In this pattern the token lives in a server environment variable and the fetch runs during server rendering — visitors only ever receive the finished HTML; the token never reaches their browser. Avoid NEXT_PUBLIC_ variables or browser-side fetches for the token: anything shipped to the browser can be read by anyone.
If a token does leak, the exposure is limited by design: it is read-only and only returns listings already publicly displayed on your website. Revoke it instantly from the same settings page (revoked tokens are rejected on the very next request); usage is metered per day.
List listings
GET /api/v1/inventory
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number, default 1 |
limit | integer | Items per page, default 20, max 100 |
status | string | Default ACTIVE. Pass SOLD for published sold units (a "recently sold" section) |
category | string | Category slug, e.g. excavators |
search | string | Matches title, make, model and stock number |
curl "https://ferroyard.com/api/v1/inventory?page=1&limit=20&category=excavators" \
-H "Authorization: Bearer fy_live_YOUR_TOKEN"Response — summaries with the primary public photo:
{
"success": true,
"data": [
{
"id": "cmq…",
"stockNumber": "FY-0001",
"slug": "2019-caterpillar-336-excavator-ab-fy-0001",
"title": "2019 Caterpillar 336 Excavator",
"listPrice": 215000,
"currency": "CAD",
"status": "ACTIVE",
"year": 2019,
"manufacturer": "Caterpillar",
"model": "336",
"specifications": { "hours": "4,100", "undercarriage": "80%" },
"category": { "id": "cmq…", "name": "Excavators", "slug": "excavators" },
"location": { "name": "Edmonton Yard", "province": "AB" },
"description": "…",
"primaryPhoto": { "url": "https://…", "thumbnailUrl": "https://…", "altText": null },
"featured": false,
"seller": { "name": "Foothills Mining Corp", "location": "Edmonton, AB" },
"createdAt": "2026-06-11T…", "updatedAt": "2026-06-12T…"
}
],
"pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3, "hasNext": true, "hasPrev": false }
}Get one listing
GET /api/v1/inventory/{idOrSlug} — accepts the listing id, stockNumber or slug interchangeably, so your listing-page URLs can use whichever you prefer.
# by id, stock number, or slug — all three work
curl "https://ferroyard.com/api/v1/inventory/FY-0001" \
-H "Authorization: Bearer fy_live_YOUR_TOKEN"The detail adds to the summary shape: photos (all public photos, primary first, with full-size and thumbnail URLs), photoCount, listingText (the written ad copy), specificationTemplate(the category's field layout, handy for rendering spec tables), videoUrl and dateListedOnline/dateSold.
Errors
| Status | Meaning |
|---|---|
401 | Missing, invalid, revoked or expired token |
403 | Origin not allowed for this token, or the token lacks the inventory:read scope |
404 | Listing not found — or exists but isn't published |
500 | Server error — safe to retry |
Every error body has the same shape: { "success": false, "error": "…" }
CORS & security
- Server-to-server calls (recommended) need no CORS setup and always work — even when an origin allow-list is configured.
- For browser calls, a token can be restricted to specific origins in Settings → Workflow → Integrations (bare domains;
www.is matched automatically; wildcards like*.example.comsupported) — browser requests from other origins get403. Without a restriction list, all origins are accepted. - Tokens are stored hashed (SHA-256) and can be revoked at any time.
OpenAPI spec
A machine-readable OpenAPI 3.0 description lives at /api/v1/openapi — point your code generator or API client (Postman, Insomnia, Bruno) at it.