PUT /api/listings creates or updates a complete listing in one atomic call: the API, its endpoints, the origin secret, plans, FAQs and linked APIs. Either everything is saved or nothing is. It is what the provider_* MCP tools use under the hood, and you can call it directly from scripts or CI.
Access
- Any account with a verified email. Unverified or blocked accounts get
403. - Authenticate with your API key:
Authorization: Bearer YOUR_API_KEY. - Rate limits per account: 30 upserts and 120 reads per minute.
- New listings count against the new-API quota. Updates never do.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| PUT | /api/listings |
Create or update a listing (or delete it with "delete": true) |
| GET | /api/listings |
Your listings (status, page, per_page) |
| GET | /api/listings/{product_key} |
Full definition of one listing (never the origin secret) |
| GET | /api/listings/me |
Your account, limits, valid plan names and parameter types |
| GET | /api/listings/categories |
Category ids |
Example
curl -X PUT https://zylalabs.com/api/listings \
-H "Authorization: Bearer $ZYLA_API_KEY" \
-H "Content-Type: application/json" \
-d @listing.json
listing.json:
{
"product_key": "trustpilot-api",
"publish": true,
"api": {
"name": "Trustpilot Reviews API",
"short_description": "Retrieve Trustpilot company reviews, ratings and reviewer details in JSON.",
"trial": false
},
"access": { "in": "header", "key": "X-Origin-Key", "value": "your-origin-secret" },
"endpoints": [{
"name": "Get Company Reviews",
"description": "Returns the latest public reviews for a company domain with pagination.",
"method": "GET",
"base_url": "https://origin.example.com/v1/items/||domain||",
"path_params": { "domain": "example.com" },
"parameters": [
{ "key": "page", "type": "number", "description": "Page number, starting at 1.", "value": "1", "required": false }
],
"headers": [{ "key": "Accept", "value": "application/json" }],
"example_response": { "reviews": [{ "rating": 5 }] }
}],
"plans": [
{ "name": "Basic", "interval": "Monthly", "price": 24.99, "max_requests": 1000 },
{ "name": "Pro", "interval": "Monthly", "price": 49.99, "max_requests": 5000 },
{ "name": "ProPlus", "interval": "Monthly", "price": 99.99, "max_requests": 15000, "rate_limit": 120 }
],
"faqs": [{ "question": "What data does it return?", "answer": "Public reviews with rating and text." }],
"slaves": [{ "name": "Company Reviews Lookup API" }]
}
Rules
Identity. product_key is your own stable id for the listing. A new key creates the listing; a key you already used updates it. A key belongs to the account that created it (403 for anyone else). Deleted listings do not accept edits (409).
Publishing. The listing goes live when it passes the quality rules and has at least one endpoint with an example response and active plans. Without those, "publish": true returns 422. Omit publish to publish as soon as it is ready, or send "publish": false to keep it as a draft. false never unpublishes a live listing.
API fields. name follows the name rules: up to 50 characters, includes the word "API", unique on the hub. short_description at least 50 characters. Zyla completes the rest of the listing page for you: the long description is built from your short description, and the category and tags are chosen with AI. If you send long_description, category_id, tags or website_url, they are ignored and listed in warnings.
Endpoints. When endpoints is present it is the full list: endpoints are matched by method + base_url (without query string), new ones are created and the ones missing from the list are deleted. Omit endpoints to leave them untouched.
- Description at least 40 characters. Method GET, POST, PUT, DELETE or PATCH.
base_urlmust be a valid URL. Path parameters go between||, as in/items/||domain||, with an example value inpath_params.- Parameters need
key,type(string, number, email, url, domain, date, datetime) and a description of at least 10 characters. example_responseis required, up to 10,000 characters.
Origin secret (access). A header or query parameter that Zyla adds to every call to your origin, so your server only accepts traffic from the hub. It is stored hidden and never returned or logged. Omit it to keep the current one; send null to remove it.
Plans. Exact names: Free, Basic, Pro, ProPlus, Premium, Elite, Ultimate, Pay as you go. price in USD; yearly plans carry their final yearly price. max_requests per cycle; a cheaper plan must include fewer requests than a more expensive one in the same interval. rate_limit from 60 to 240 per minute. At least three paid monthly plans must stay active. If you send no plans on a new listing, a default set is created.
FAQs. Full list when present, up to 10. Question 5–100 characters, answer 3–700.
Linked APIs (slaves). Variants of your API with their own name and page that reuse its endpoints. They are matched by name and follow changes to the master's endpoints. The master needs endpoints, and a linked API cannot have linked APIs of its own.
Delete. {"product_key": "...", "delete": true} deletes the listing. It is refused with 409 while it has paid subscriptions. Linked APIs are deleted only if you name them in slaves.
Not supported. Response transformations, request bodies and file uploads (422 not_supported). Use the web wizard for those.
Responses
Success: {"success": true, "listing": {...}, "warnings": []}, with 201 on create and 200 on update. listing.url is set once the listing is live.
| Status | code |
Meaning |
|---|---|---|
| 401 | unauthenticated |
Missing or invalid key |
| 403 | forbidden |
Unverified or blocked account, or the product_key belongs to another account |
| 404 | not_found |
Unknown product_key |
| 409 | name_taken, active_subscriptions, endpoints_required, listing_deleted, slave_of_slave, product_key_conflict |
Conflict; nothing was changed |
| 422 | validation_failed, ai_validation_failed, not_supported |
errors keyed by field, e.g. endpoints.2.parameters.0.description |
| 429 | creation_limit_reached |
New-API quota used up; retry_after seconds and a Retry-After header |
| 503 | ai_unavailable |
The content check did not respond; nothing was saved; retry |
| 500 | server_error |
Unexpected failure; nothing was saved |