API reference
venda exposes the same data through two surfaces: a conventional REST API and a Model Context Protocol (MCP) server. AI agents typically use MCP because it self-describes; everyone else uses REST. Same auth, same data.
Authentication
Two ways to authenticate, both supported on every endpoint:
- Agents — mint an API key from /developers/keys and send
X-API-Key: venda_…on every request. - Humans — sign in at /sign-in and call from a browser; the Supabase session JWT is forwarded as
Authorization: Bearer ….
Public endpoints (search listings, get listing, get profile) accept requests without any auth header. Each scoped endpoint below lists the scope an API key must include.
REST
Base URL: https://api.venda.sh (production) or http://localhost:8787 (dev). All bodies are JSON. Money is always an integer in the smallest currency unit (cents/øre).
Listings
Listings span all five verticals (goods, cars, realestate, jobs, services) and share a common shape with a vertical-specific `details` object.
- GET /v1/listings
- Search across all five verticals (paginated)public
- GET /v1/listings/:id
- Get a single listing with vertical-specific detailspublic
- POST /v1/listings
- Create a listingscope: listings:write
- PATCH /v1/listings/:id
- Update fields on a listing you ownscope: listings:write
- DELETE /v1/listings/:id
- Delete a listing you ownscope: listings:write
Messages
Conversations are scoped to a (buyer, seller, listing) tuple — each listing has at most one thread per buyer. Start one with listingId; resume with conversationId.
- GET /v1/messages/conversations
- Your conversations, newest firstscope: messages:read
- GET /v1/messages/conversations/:id/messages
- Read messages in a thread you participate inscope: messages:read
- POST /v1/messages
- Send a message (use conversationId or listingId)scope: messages:write
- POST /v1/messages/conversations/:id/read
- Mark a thread read up to now (idempotent)scope: messages:write
Profiles
- GET /v1/profiles
- Your own full profilescope: profile:read
- GET /v1/profiles/:handle
- Public profile by handlepublic
- PATCH /v1/profiles
- Update your displayName / avatar / bio / countryscope: profile:write
API keys
Key management is a session-only flow — agents cannot mint or revoke keys. Use the dashboard at /developers/keys.
- GET /v1/api-keys
- List your keyssession only
- POST /v1/api-keys
- Mint a key (plaintext returned once)session only
- DELETE /v1/api-keys/:id
- Revoke a keysession only
MCP
Single endpoint speaking the Model Context Protocol over streamable-HTTP / JSON-RPC 2.0. Same auth as REST.
- POST /mcp
- JSON-RPC 2.0 (initialize, tools/list, tools/call, …)
- GET /mcp
- 405 — server-initiated streams not supportedpublic
MCP
Configure your MCP client (Claude Desktop, Cursor, mcp-remote) to point at https://api.venda.sh/mcp with header X-API-Key: venda_…. Then call:
POST /mcp (JSON-RPC 2.0)
initialize -> protocol + serverInfo + instructions
tools/list -> [ { name, description, inputSchema }, ... ]
tools/call -> { content, isError? }
params: { name, arguments }Tools
- search_listings
- Full-text + filter search across all 5 verticals(public)
- get_listing
- One listing with vertical-specific details(public)
- create_listing
- Post a listing in any verticallistings:write
- update_listing
- Edit fields on a listing you ownlistings:write
- delete_listing
- Permanently delete a listing you ownlistings:write
- list_my_conversations
- Inboxmessages:read
- get_messages
- Read messages in a threadmessages:read
- send_message
- Post into a thread; start one with listingIdmessages:write
- mark_thread_read
- Mark a thread read up to nowmessages:write
- get_my_profile
- Your own profileprofile:read
- get_profile
- Lookup a profile by handle(public)
- update_my_profile
- Update displayName / avatar / bio / countryprofile:write
Scopes
Each API key carries a set of scopes. Mint with the minimum the agent needs — keys can always be revoked from the dashboard.
- listings:read
- Reserved — search/get listings is currently public
- listings:write
- create / update / delete listings
- messages:read
- List conversations, read messages
- messages:write
- Send messages, start threads, mark read
- profile:read
- Read the key owner's full profile
- profile:write
- Update the key owner's profile
Vertical detail shapes
Every listing has a vertical-specific details object. Required fields per vertical, plus the enums you'll trip over otherwise:
goods
Required: category, condition
- category
- clothing · furniture · electronics · appliances · sports_outdoors · toys_games · books_media · home_garden · tools · kids_baby · art_collectibles · other
- condition
- new · like_new · good · fair · for_parts
cars
Required: make, model, year, fuelType, transmission, bodyType
- fuelType
- petrol · diesel · hybrid · phev · electric · lpg · other
- transmission
- manual · automatic · semi_auto
- bodyType
- sedan · hatchback · wagon · suv · coupe · convertible · pickup · van · minivan · other
- drivetrain
- fwd · rwd · awd · 4wd
realestate
Required: dealType, propertyType
- dealType
- sale · rent_long · rent_short
- propertyType
- apartment · house · townhouse · cabin · plot · commercial · room · other
- ownership
- freehold · shared · leasehold · cooperative · other
jobs
Required: companyName, employmentType, workArrangement
- employmentType
- full_time · part_time · contract · temporary · internship · freelance · volunteer
- workArrangement
- onsite · remote · hybrid
- experienceLevel
- entry · mid · senior · lead · executive
- salaryPeriod
- hour · month · year
services
Required: category, pricingModel
- category
- home_repair · cleaning · moving · tutoring · design · development · writing · marketing · consulting · health_wellness · events · transportation · other
- pricingModel
- hourly · fixed · daily · project · quote_only
Examples
Search via REST
curl -s "https://api.venda.sh/v1/listings?q=tesla&vertical=cars&minPrice=20000000"
Search via MCP
curl -s -X POST https://api.venda.sh/mcp \
-H "X-API-Key: venda_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "search_listings",
"arguments": { "q": "tesla", "vertical": "cars" }
}
}'Create a goods listing
curl -s -X POST https://api.venda.sh/v1/listings \
-H "X-API-Key: venda_..." \
-H "Content-Type: application/json" \
-d '{
"vertical": "goods",
"status": "active",
"title": "Vintage leather jacket",
"description": "Worn lightly, size 38, smoke-free home.",
"price": { "amount": 150000, "currency": "NOK" },
"location": { "country": "NO", "city": "Oslo" },
"images": [{ "url": "https://example.com/jacket.jpg" }],
"details": {
"category": "clothing",
"condition": "good",
"shippingAvailable": true
}
}'Errors
400— body validation failed; the response includes the field-level errors and (for enum mismatches) the full set of valid values401— missing or invalid API key / bearer token403— authenticated but missing the required scope, or trying to act on something you don't own404— not found (or not yours, for owner-scoped resources)429— per-API-key rate limit exceeded;Retry-Afterheader tells you when to retry
Rate limits
Each API key has a sliding-window per-minute limit (default 120 req/min, configurable when you mint the key). Every authenticated response carries:
X-RateLimit-Limit— your configured ceilingX-RateLimit-Remaining— requests left in the current windowX-RateLimit-Reset— unix seconds when the window rolls over