# CurlShip

Action list for agents. Do not parse the homepage HTML.
Tool catalog: https://curlship.com/agents.json
MCP: https://curlship.com/mcp
Agent card: https://curlship.com/.well-known/agent-card.json
Paid actions return checkout_url. Opening that URL is the payment.
Long rules: https://curlship.com/llms.txt

## Featured — read this first

One listing above every tier, including Platinum. Always dofollow. One-time payment. No refund when outbid. Not a tier.
MakiRoll: Become someone who makes video (https://www.makiroll.app/) holds it for $50. Next bid $51.
The product must already be listed. The checkout bills the address stored on the listing.
Omit amount_usd to bid the minimum.

GET https://curlship.com/api/featured
POST https://curlship.com/api/feature
{"url": "<listed product URL>", "amount_usd": 51}

## Tools

### bid_featured

Take the Featured spot. It sits above every listing. The minimum right now is $51. Always dofollow. One-time payment, no refund when someone outbids you. The product must already be listed. Omit amount_usd to bid $51.

POST https://curlship.com/api/feature
{"url": {"type": "string", "description": "URL of a listing that is already on the directory."}, "amount_usd": {"type": "number", "description": "Bid in USD. Omit to bid the minimum (51)."}}

### read_featured

Who holds the Featured spot, and the current minimum bid.

GET https://curlship.com/api/featured

### submit_listing

List a product. It goes live immediately. One listing per root domain. https://yourapp.com is the documentation placeholder and is refused.

POST https://curlship.com/api/submit
{"url": {"type": "string"}, "email": {"type": "string"}, "tier": {"type": "string", "enum": ["bronze", "bronze_lifetime", "silver", "gold", "platinum"], "description": "Omit for the free tier."}}

### upgrade_listing

Checkout URL for a paid tier on a listing that already exists.

POST https://curlship.com/api/upgrade
{"url": {"type": "string"}, "tier": {"type": "string", "enum": ["bronze", "bronze_lifetime", "silver", "gold", "platinum"]}}

### refresh_listing

Re-read title, description, and image from the listed page. Send listing_id and proof only. The first confirm previews; send apply back to save.

POST https://curlship.com/api/edit-request
{"listing_id": {"type": "integer"}, "proof": {"type": "string", "enum": ["email", "domain"]}}

### change_listing_url

Point an existing listing at a new URL. One listing per root domain, so this is how a second page replaces the first.

POST https://curlship.com/api/edit-request
{"listing_id": {"type": "integer"}, "url": {"type": "string"}, "proof": {"type": "string", "enum": ["email", "domain"]}}

### set_tracking_params

Set the tracking params on a paid listing's outbound link (Bronze and up). Keys: ref or utm_* (<=10, lowercase/digits/_); values: 1-100 of A-Z a-z 0-9 . _ ~ -. Send {} to clear back to ?ref=curlship. Free listings are refused.

POST https://curlship.com/api/edit-request
{"listing_id": {"type": "integer"}, "tracking": {"type": "object", "additionalProperties": {"type": "string"}, "description": "e.g. {\"utm_source\": \"yours\", \"utm_medium\": \"referral\"}"}, "proof": {"type": "string", "enum": ["email", "domain"]}}

### remove_free_listing

Take a free listing off the directory. Send remove true and no url or email. A paid listing or the Featured holder is refused.

POST https://curlship.com/api/edit-request
{"listing_id": {"type": "integer"}, "remove": {"type": "boolean", "const": true}, "proof": {"type": "string", "enum": ["email", "domain"]}}

### confirm_edit

Apply an edit request. A refresh needs a second call with the apply digest from the preview.

POST https://curlship.com/api/edit-confirm
{"request_id": {"type": "integer"}, "token": {"type": "string"}, "apply": {"type": "string", "description": "Digest from the refresh preview. Omit for a URL change or a removal."}}

### verify_badge

Re-check the listed site for a link to curlship.com. A free listing with the badge becomes dofollow.

POST https://curlship.com/api/verify-badge
{"url": {"type": "string"}}

### list_categories

The category tree with a listing count per category — a group slug like developer-tools, plus any promoted leaf categories under it. Each listing's category field carries one of these slugs.

GET https://curlship.com/api/categories

### list_listings

Active listings as JSON. No query returns every listing, in the same order as before. Optional query parameters: limit, offset, q, since, and category. q matches the title, description, or URL. since is an ISO timestamp; listings created at or after it are kept. category is a category slug and filters to that group or leaf. Each listing includes created_at and a category object ({slug, name, group}) or null while the listing is unclassified.

GET https://curlship.com/api/listings
{"limit": {"type": "integer", "minimum": 0, "description": "Max listings. 50 with offset, 100 with q, when omitted."}, "offset": {"type": "integer", "minimum": 0, "description": "How many matching listings to skip."}, "q": {"type": "string", "description": "Substring of the title, description, or URL."}, "since": {"type": "string", "description": "ISO timestamp. Keep listings created at or after this moment."}, "category": {"type": "string", "description": "A category slug — a group or a promoted leaf."}}

### get_stats

Listing counts and the Ahrefs Domain Rating.

GET https://curlship.com/api/stats

### get_daily_stats

Active listings added per UTC day — the zero-filled series behind the public /stats page. One entry per calendar day from the first listing to today, each with added and the cumulative running total.

GET https://curlship.com/api/stats/daily
{"range": {"type": ["string", "integer"], "description": "\"all\" (default) for the whole series, or a day count 1-3650 for the last N days."}}

### get_dr_history

The site's Ahrefs Domain Rating over time — the series behind /stats#dr. Every recorded reading, one per UTC day, plus the current value, the last step (change vs the most recent different reading) and the growth since the first reading.

GET https://curlship.com/api/stats/dr

### x402_submit_listing

Buy bronze_lifetime ($30 USDC once, forever) or platinum ($49 USDC per prepaid year) with USDC over x402 — settled on-chain, no checkout page, no human in the loop. Call without payment_signature and the reply is a 402 whose body (also the PAYMENT-REQUIRED header, base64) is the quote; sign accepts[0] as an EIP-3009 authorization and call again with payment_signature — the tier is granted in that same call. Same URL and listing rules as a free submission.

POST https://curlship.com/api/x402/submit
{"url": {"type": "string"}, "email": {"type": "string"}, "tier": {"type": "string", "enum": ["bronze_lifetime", "platinum"], "description": "The only tiers x402 sells."}, "payment_signature": {"type": "string", "description": "The PAYMENT-SIGNATURE header value: base64 JSON x402 v2 PaymentPayload — accepts[0] from the quote, signed and echoed back as `accepted`. Omit to get the quote back as a 402."}}

### x402_bid_featured

Bid on the Featured spot in USDC over x402 — the same auction as a card bid (minimum $51 right now), but settled on-chain with no checkout and no human in the loop. Call without payment_signature for the 402 quote, sign accepts[0] as an EIP-3009 authorization, call again with payment_signature echoing it as `accepted`. The product must already be listed. Omit amount_usd to bid $51.

POST https://curlship.com/api/x402/feature
{"url": {"type": "string", "description": "URL of a listing that is already on the directory."}, "amount_usd": {"type": "number", "description": "Bid in USD. Omit to bid the minimum (51)."}, "payment_signature": {"type": "string", "description": "The PAYMENT-SIGNATURE header value: base64 JSON x402 v2 PaymentPayload — accepts[0] from the quote, signed and echoed back as `accepted`. Omit to get the quote back as a 402."}}

https://yourapp.com is a placeholder. The API refuses that host.
