Launch
Preview, launch and follow the listings. GET /projects/{project_id}/readiness is free and shows what a launch would do; POST /projects/{project_id}/launch uses 1 launch and creates up to 100 listings, one per directory.
A listing moves pending → in_progress → submitted → live, or ends not_accepted or cancelled. When it waits on you or your client it shows action_required with an action to take. Listing times are rounded down to the hour.
Shows for free what a launch would do now: whether the project is ready, how many directories it would get, and what would unlock more. On a launched project it previews what POST /projects/{project_id}/listings would add from the free allowance. The numbers follow the catalog and can change from one hour to the next.
project_id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$The preview.
application/json- response
A free preview of what a launch, or for a launched project an add, would do now.
object*stringAlways readiness.
livemode*booleantrue for a live project, false for a test project.
project*stringThe project's id.
^prj_[0-9A-Za-z]{22}$ready*booleantrue when a launch would go through now.
missing*array<string>What a launch still needs: contact_email, logo or screenshots (at least 1). Empty when ready is true.
directories*The directories the launch would pick now.
unlocks*array<>Changes that would add directories, most directories first.
not_matching*Directories that do not take this kind of product, by reason. If a reason is wrong for the product, fix audience.
cost*What the launch would use.
Typical errors
curl "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness" \ -H "Authorization: Bearer $SUBMITATOR_API_KEY"{ "object": "readiness", "livemode": false, "project": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "ready": true, "missing": [], "directories": { "selected": 87, "limit": 100, "top_domain_rating": 92, "median_domain_rating": 48 }, "unlocks": [ { "requirement": "badge", "directories": 9, "how": "Install the badge on https://ledgerly.test and verify it." }, { "requirement": "competitors", "directories": 3, "how": "Add at least 1 competitor in competitors." }, { "requirement": "promo_code", "directories": 1, "how": "Add a promo code in promo_code." } ], "not_matching": { "b2b_only": 4, "ai_only": 7, "directories_only": 2, "other": 0 }, "cost": { "launches": 1, "available": 100 }}Uses 1 launch and creates a listing in pending for each directory picked, up to 100. Send "auto" to take the best directories the project qualifies for, include to name them, or exclude to leave some out. A failed call spends nothing, and a second launch of the same project returns 409 already_launched. Listings beyond your capacity of 100 in progress wait in pending and start as others finish.
project_id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$Idempotency-Key?stringA unique string, such as a UUID, that makes a retry safe. A success is kept for 24 hours: a repeat with the same key and body returns it again with Idempotent-Replayed: true, and the same key with another body returns 422 idempotency_key_reused. A repeat while the first request runs returns 409 idempotency_key_in_use. An error changes nothing, so a repeat after one runs again.
1 <= length <= 255application/json- body
How to launch. An empty body launches with auto.
directories?||Which directories to launch with. Defaults to auto.
auto_replace?booleanSets the project's auto_replace. Leave it out to keep the project's value, except that a launch with include sets it to false.
Launched. The response is the project right after the launch, answering the auto request; listings move on their own from here.
application/json- response
One of your client's products, from draft to the last listing.
object*stringAlways project.
id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$livemode*booleantrue for a live project, false for a test project.
status*stringWhere the project stands. When more than one fits, the first in this list wins:
on_hold: work is paused;hold_reasonsays why.action_required: you or your client need to act;actionsays what.in_progress: listings are moving and nobody needs to act.completed: no listing ispending,in_progressoraction_required, and auto-replace has nothing to add. Listings insubmittedcan still go live.draft: not launched yet.
The console and reports show these as Draft, In progress, Needs you, On hold and Completed.
"draft""in_progress""action_required""on_hold""completed"hold_reason*|Why work is paused, when status is on_hold: review while we review the project, which clears without action from you, or paused when we paused it. null otherwise.
"review""paused"nullaction*|What to do, when status is action_required. null otherwise.
url*stringYour client's site, http or https, up to 2,048 characters. Unique among your projects in the mode. Live mode accepts public addresses only: no IP addresses and no .test, .example, .invalid or .localhost hosts.
urilength <= 2048name*stringThe product's name as directories show it, 2 to 100 characters. Until you set one, it comes from the domain, and once autofill finishes, from the site's title.
2 <= length <= 100maker_name*|The person directories list as the maker, up to 100 characters.
length <= 100contact_email*|Required to launch. Some directories register the listing to this address and send their emails here. Unique among your projects in the mode, and fixed once the project launches.
emaillength <= 254tagline*|One line about the product, up to 100 characters.
length <= 100short_description*|A short description, up to 500 characters. Most directories show this one.
length <= 500long_description*|A longer description, up to 2,000 characters, for directories that take one.
length <= 2000category*|The product's category. We map it to each directory's own list of categories.
"saas""ai_tools""dev_tools""no_code""productivity""other"nullpricing_model*|How the product is sold.
"free""freemium""paid""open_source"nullprice_amount_cents*|The headline price in US cents, such as 1200 for $12. Leave it null for a free product.
0 <= valueprice_period*|What price_amount_cents pays for.
"monthly""yearly""one_time"nulllaunch_date*|The day the product went public, YYYY-MM-DD, for directories that ask for it. Not the day the project launched here: that is launched_at.
datetwitter_url*|The product's profile on X, as an https://x.com/ or https://twitter.com/ URL.
^https://(x|twitter)\.com/.+uricompetitors*array<>Up to 20 products this one is an alternative to. Some directories list a product only next to its competitors. Sending a list replaces the whole list.
items <= 20tags*array<>Up to 10 keywords of up to 40 characters each. Sending a list replaces the whole list.
items <= 10audience*Marks that open the directories which accept only some kinds of products.
promo_code*|A discount code for the product, up to 50 characters. Some directories list only products with one.
length <= 50promo_description*|What promo_code gives, up to 500 characters, such as 30% off the first 3 months.
length <= 500logo*|The logo, or null when the project has none.
screenshots*array<>0 to 5 screenshots, in the order directories use them.
items <= 5autofill*What we read from the client's site to fill in the project.
readiness*Whether the project can launch. GET /projects/{project_id}/readiness shows the full preview.
domain_rating*|The client's site's Domain Rating from Ahrefs, 0 to 100. null until it is measured, which starts after your first purchase.
0 <= value <= 100badge*The badge on the client's site, as of the last check. GET /projects/{project_id}/badge has the install kit.
auto_replace*booleanNew projects start with true. When true, we add fitting directories from the free allowance for you: after a listing ends not_accepted or cancelled, after the badge passes verification, and when a new directory joins the catalog. A launch with include turns it off unless the launch sends auto_replace: true.
excluded_directories*array<>Directories never to use for this project: auto launches, auto adds and auto-replace skip them. A launch with exclude adds its list here. Sending a list replaces the whole list.
allowance*|How the launch's 100 directories are spent. null before launch.
progress*|How many listings are in each status. null before launch.
external_id*|Your own id for the project, up to 100 characters, such as the client's id in your CRM. Unique among your projects in the mode; find a project by it with GET /projects?external_id=.
length <= 100metadata*Your own key-value data. We store it and return it, and use it for nothing else.
properties <= 20created_at*stringWhen the project was created.
date-timeupdated_at*stringWhen a field, the status or a listing count last changed.
date-timelaunched_at*|When the project launched. null for a draft.
date-timecurl -X POST "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/launch" \ -H "Authorization: Bearer $SUBMITATOR_API_KEY" \ -H "Idempotency-Key: 4f9d2c1e-ledgerly-create" \ -H "Content-Type: application/json" \ -d '{"directories":"auto"}'{ "object": "project", "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "livemode": false, "status": "in_progress", "hold_reason": null, "action": null, "url": "https://ledgerly.test", "name": "Ledgerly", "maker_name": "Maya Chen", "contact_email": "founder@ledgerly.test", "tagline": "Double-entry bookkeeping for solo founders", "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.", "long_description": null, "category": "saas", "pricing_model": "freemium", "price_amount_cents": 1200, "price_period": "monthly", "launch_date": null, "twitter_url": null, "competitors": [], "tags": [ "accounting", "bookkeeping", "invoicing" ], "audience": { "b2b": false, "ai": false, "directory": false }, "promo_code": null, "promo_description": null, "logo": { "object": "image", "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ", "status": "ready", "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4", "error": null }, "screenshots": [ { "object": "image", "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF", "status": "ready", "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7", "error": null } ], "autofill": { "status": "complete", "name_source": "site", "filled": [ "name", "tagline", "short_description" ], "image_candidates": [ { "url": "https://ledgerly.test/apple-touch-icon.png", "suggested_as": "logo", "width": 180, "height": 180 }, { "url": "https://ledgerly.test/og-image.png", "suggested_as": "screenshot", "width": 1200, "height": 630 } ] }, "readiness": { "ready": true, "missing": [] }, "domain_rating": 34, "badge": { "status": "not_checked", "checked_at": null }, "auto_replace": true, "excluded_directories": [], "allowance": { "total": 100, "used": 0, "reserved": 87, "available": 13 }, "progress": { "total": 87, "pending": 87, "in_progress": 0, "action_required": 0, "submitted": 0, "live": 0, "not_accepted": 0, "cancelled": 0 }, "external_id": "crm_8812", "metadata": { "account_owner": "Priya", "crm_stage": "onboarding" }, "created_at": "2026-10-03T13:58:12Z", "updated_at": "2026-10-03T14:05:40Z", "launched_at": "2026-10-03T14:05:40Z"}Returns one listing per directory, highest Domain Rating first. Filter by status or directory. Listing times are rounded down to the hour.
project_id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$status?stringOnly listings in this status.
"pending""in_progress""action_required""submitted""live""not_accepted""cancelled"directory?stringOnly the listing at this directory.
^dir_[0-9A-Za-z]{22}$cursor?stringnext_cursor from the previous page of the same list, as you received it. Leave it out for the first page.
length <= 500limit?integerHow many items to return, 1 to 100. Defaults to 20.
1 <= value <= 10020One page of listings.
application/json- response
One page of a list. Listings, highest Domain Rating first.
object*stringAlways list.
data*array<>The items on this page.
has_more*booleantrue when there is another page.
next_cursor*|Send it as cursor for the next page. null when has_more is false.
length <= 500Typical errors
curl "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/listings" \ -H "Authorization: Bearer $SUBMITATOR_API_KEY"{ "object": "list", "data": [ { "object": "listing", "id": "lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK", "livemode": false, "project": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "directory": { "id": "dir_2Lm8Wq3Zk7Rt1Xc5Vb9NdF", "name": "BetaList", "domain_rating": 73, "logo_url": "https://feed.example.net/a/x1Lq7Rt.k9Zm" }, "status": "live", "reason": null, "action": null, "live_url": "https://betalist.com/startups/ledgerly", "proof_url": "https://feed.example.net/a/Pf8Kq2Wm.t6Lx9", "created_at": "2026-10-03T14:00:00Z", "submitted_at": "2026-10-04T09:00:00Z", "live_at": "2026-10-08T14:00:00Z" } ], "has_more": true, "next_cursor": "cur_8Tq2Xn5Wb1Lz7Pd3"}Adds directories to a launched project from its free allowance, the part that listings neither use nor reserve. A directory that already has a listing on the project cannot be added again. With auto_replace on, this happens for you whenever allowance frees up or a fitting directory joins the catalog.
project_id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$Idempotency-Key?stringA unique string, such as a UUID, that makes a retry safe. A success is kept for 24 hours: a repeat with the same key and body returns it again with Idempotent-Replayed: true, and the same key with another body returns 422 idempotency_key_reused. A repeat while the first request runs returns 409 idempotency_key_in_use. An error changes nothing, so a repeat after one runs again.
1 <= length <= 255application/json- body
Which directories to add. An empty body adds with auto.
directories?||Which directories to add, up to the free allowance. Defaults to auto.
Added. The new listings are in pending; this example answers an auto request that added 9.
application/json- response
One of your client's products, from draft to the last listing.
object*stringAlways project.
id*stringThe project's id.
^prj_[0-9A-Za-z]{22}$livemode*booleantrue for a live project, false for a test project.
status*stringWhere the project stands. When more than one fits, the first in this list wins:
on_hold: work is paused;hold_reasonsays why.action_required: you or your client need to act;actionsays what.in_progress: listings are moving and nobody needs to act.completed: no listing ispending,in_progressoraction_required, and auto-replace has nothing to add. Listings insubmittedcan still go live.draft: not launched yet.
The console and reports show these as Draft, In progress, Needs you, On hold and Completed.
"draft""in_progress""action_required""on_hold""completed"hold_reason*|Why work is paused, when status is on_hold: review while we review the project, which clears without action from you, or paused when we paused it. null otherwise.
"review""paused"nullaction*|What to do, when status is action_required. null otherwise.
url*stringYour client's site, http or https, up to 2,048 characters. Unique among your projects in the mode. Live mode accepts public addresses only: no IP addresses and no .test, .example, .invalid or .localhost hosts.
urilength <= 2048name*stringThe product's name as directories show it, 2 to 100 characters. Until you set one, it comes from the domain, and once autofill finishes, from the site's title.
2 <= length <= 100maker_name*|The person directories list as the maker, up to 100 characters.
length <= 100contact_email*|Required to launch. Some directories register the listing to this address and send their emails here. Unique among your projects in the mode, and fixed once the project launches.
emaillength <= 254tagline*|One line about the product, up to 100 characters.
length <= 100short_description*|A short description, up to 500 characters. Most directories show this one.
length <= 500long_description*|A longer description, up to 2,000 characters, for directories that take one.
length <= 2000category*|The product's category. We map it to each directory's own list of categories.
"saas""ai_tools""dev_tools""no_code""productivity""other"nullpricing_model*|How the product is sold.
"free""freemium""paid""open_source"nullprice_amount_cents*|The headline price in US cents, such as 1200 for $12. Leave it null for a free product.
0 <= valueprice_period*|What price_amount_cents pays for.
"monthly""yearly""one_time"nulllaunch_date*|The day the product went public, YYYY-MM-DD, for directories that ask for it. Not the day the project launched here: that is launched_at.
datetwitter_url*|The product's profile on X, as an https://x.com/ or https://twitter.com/ URL.
^https://(x|twitter)\.com/.+uricompetitors*array<>Up to 20 products this one is an alternative to. Some directories list a product only next to its competitors. Sending a list replaces the whole list.
items <= 20tags*array<>Up to 10 keywords of up to 40 characters each. Sending a list replaces the whole list.
items <= 10audience*Marks that open the directories which accept only some kinds of products.
promo_code*|A discount code for the product, up to 50 characters. Some directories list only products with one.
length <= 50promo_description*|What promo_code gives, up to 500 characters, such as 30% off the first 3 months.
length <= 500logo*|The logo, or null when the project has none.
screenshots*array<>0 to 5 screenshots, in the order directories use them.
items <= 5autofill*What we read from the client's site to fill in the project.
readiness*Whether the project can launch. GET /projects/{project_id}/readiness shows the full preview.
domain_rating*|The client's site's Domain Rating from Ahrefs, 0 to 100. null until it is measured, which starts after your first purchase.
0 <= value <= 100badge*The badge on the client's site, as of the last check. GET /projects/{project_id}/badge has the install kit.
auto_replace*booleanNew projects start with true. When true, we add fitting directories from the free allowance for you: after a listing ends not_accepted or cancelled, after the badge passes verification, and when a new directory joins the catalog. A launch with include turns it off unless the launch sends auto_replace: true.
excluded_directories*array<>Directories never to use for this project: auto launches, auto adds and auto-replace skip them. A launch with exclude adds its list here. Sending a list replaces the whole list.
allowance*|How the launch's 100 directories are spent. null before launch.
progress*|How many listings are in each status. null before launch.
external_id*|Your own id for the project, up to 100 characters, such as the client's id in your CRM. Unique among your projects in the mode; find a project by it with GET /projects?external_id=.
length <= 100metadata*Your own key-value data. We store it and return it, and use it for nothing else.
properties <= 20created_at*stringWhen the project was created.
date-timeupdated_at*stringWhen a field, the status or a listing count last changed.
date-timelaunched_at*|When the project launched. null for a draft.
date-timeTypical errors
curl -X POST "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/listings" \ -H "Authorization: Bearer $SUBMITATOR_API_KEY" \ -H "Idempotency-Key: 4f9d2c1e-ledgerly-create" \ -H "Content-Type: application/json" \ -d '{"directories":"auto"}'{ "object": "project", "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "livemode": false, "status": "in_progress", "hold_reason": null, "action": null, "url": "https://ledgerly.test", "name": "Ledgerly", "maker_name": "Maya Chen", "contact_email": "founder@ledgerly.test", "tagline": "Double-entry bookkeeping for solo founders", "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.", "long_description": null, "category": "saas", "pricing_model": "freemium", "price_amount_cents": 1200, "price_period": "monthly", "launch_date": null, "twitter_url": null, "competitors": [], "tags": [ "accounting", "bookkeeping", "invoicing" ], "audience": { "b2b": false, "ai": false, "directory": false }, "promo_code": null, "promo_description": null, "logo": { "object": "image", "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ", "status": "ready", "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4", "error": null }, "screenshots": [ { "object": "image", "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF", "status": "ready", "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7", "error": null } ], "autofill": { "status": "complete", "name_source": "site", "filled": [ "name", "tagline", "short_description" ], "image_candidates": [ { "url": "https://ledgerly.test/apple-touch-icon.png", "suggested_as": "logo", "width": 180, "height": 180 }, { "url": "https://ledgerly.test/og-image.png", "suggested_as": "screenshot", "width": 1200, "height": 630 } ] }, "readiness": { "ready": true, "missing": [] }, "domain_rating": 34, "badge": { "status": "installed", "checked_at": "2026-10-04T09:58:03Z" }, "auto_replace": true, "excluded_directories": [], "allowance": { "total": 100, "used": 53, "reserved": 39, "available": 8 }, "progress": { "total": 96, "pending": 9, "in_progress": 30, "action_required": 0, "submitted": 31, "live": 22, "not_accepted": 3, "cancelled": 1 }, "external_id": "crm_8812", "metadata": { "account_owner": "Priya", "crm_stage": "onboarding" }, "created_at": "2026-10-03T13:58:12Z", "updated_at": "2026-10-04T10:02:15Z", "launched_at": "2026-10-03T14:05:40Z"}Returns one listing with its directory, its status and, once live, its URL. A listing keeps its id for the life of the project, including after a correction.
listing_id*stringThe listing's id.
^lst_[0-9A-Za-z]{22}$The listing.
application/json- response
One project at one directory. Its times are rounded down to the hour.
object*stringAlways listing.
id*stringThe listing's id. A project has 1 listing per directory, and it keeps its id after a correction.
^lst_[0-9A-Za-z]{22}$livemode*booleantrue for a live listing, false for a test listing.
project*stringThe project's id.
^prj_[0-9A-Za-z]{22}$directory*The directory.
status*stringWhere the listing stands:
pending: waiting to start. Listings start as capacity frees: up to 100 of yours are in progress at once.in_progress: being prepared and sent to the directory.action_required: waiting on you or your client;actionsays what.submitted: the directory has it and is reviewing it.live: published;live_urlpoints to it.not_accepted: the directory declined it, could not take it, or took it down;reasonsays which.cancelled: withdrawn before it reached the directory.
The usual path is pending → in_progress ⇄ action_required → submitted → live, and some listings go live without submitted. Rare corrections each send their own event: live → not_accepted (removed), live → submitted, and not_accepted or cancelled → pending, in_progress, submitted or live. The console and reports show the statuses as Pending, In progress, Needs you, Submitted, Live, Not accepted and Cancelled.
"pending""in_progress""action_required""submitted""live""not_accepted""cancelled"reason*|Why the listing ended, for not_accepted and cancelled; null otherwise. New values can appear.
declined: the directory reviewed the listing and said no.unavailable: the directory could not take the listing, for example because it stopped taking new products.removed: the directory took down a listing that was live.withdrawn: we withdrew the listing before it reached the directory.
action*|What to do, when status is action_required. null otherwise.
live_url*|The published listing, when status is live. null before that, and when it went live without a link.
uriproof_url*|A screenshot of the live listing, served from the badge feed host. null when there is none.
uricreated_at*stringWhen the listing was created, rounded down to the hour.
date-timesubmitted_at*|When the directory received the listing, rounded down to the hour. null before that.
date-timelive_at*|When the listing went live, rounded down to the hour. null unless it is or was live.
date-timeTypical errors
curl "https://api.submitator.com/v1/listings/lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK" \ -H "Authorization: Bearer $SUBMITATOR_API_KEY"{ "object": "listing", "id": "lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK", "livemode": false, "project": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "directory": { "id": "dir_2Lm8Wq3Zk7Rt1Xc5Vb9NdF", "name": "BetaList", "domain_rating": 73, "logo_url": "https://feed.example.net/a/x1Lq7Rt.k9Zm" }, "status": "live", "reason": null, "action": null, "live_url": "https://betalist.com/startups/ledgerly", "proof_url": "https://feed.example.net/a/Pf8Kq2Wm.t6Lx9", "created_at": "2026-10-03T14:00:00Z", "submitted_at": "2026-10-04T09:00:00Z", "live_at": "2026-10-08T14:00:00Z"}