# Launch and track

URL: https://submitator.com/docs/guides/launch-and-track

> Preview a launch, spend 1 launch on the directories a project qualifies for, and follow each listing until it is live.

## What you'll build

A launched project, and the reads that tell you where each listing stands: which ones are live and where, which ones wait on you or your client, and which ones the directory turned down. Most listings go live within a week, and in test mode within about 15 minutes.

## Before you start

- **A project that is ready.** Its `readiness.ready` is `true`: see [Onboard a client's product](https://submitator.com/docs/guides/onboard-a-clients-product).
- **1 launch on your balance.** A live launch is one you bought; test mode starts with 100 test launches.

A live launch cannot be undone. A launch that fails spends nothing.

## 1. Check your balance

`GET /account` shows the launches you have, and how many listings run across all your projects.

```bash
curl https://api.submitator.com/v1/account \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
const res = await fetch("https://api.submitator.com/v1/account", {
  headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` },
});
console.log(await res.json());
```

```python
import os
import requests

res = requests.get(
    "https://api.submitator.com/v1/account",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
)
print(res.json())
```

Response 200 (some fields left out):

```json
{
  "object": "account",
  "livemode": false,
  "state": "active",
  "launches": { "available": 97, "used": 3, "total": 100 },
  "capacity": { "in_flight": 25, "limit": 100 }
}
```

Up to `capacity.limit` listings run at once across your projects. Above it, new listings wait in `pending` and start as others finish; a launch is accepted either way.

## 2. Preview the launch

`GET /projects/{project_id}/readiness` is free: it shows how many directories a launch would get now, their top and median Domain Rating, and what it costs.

```bash
curl https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
const res = await fetch(
  "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness",
  { headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` } },
);
console.log(await res.json());
```

```python
import os
import requests

res = requests.get(
    "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
)
print(res.json())
```

Response 200 (some fields left out):

```json
{
  "object": "readiness",
  "ready": true,
  "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." }
  ],
  "cost": { "launches": 1, "available": 97 }
}
```

The numbers follow the catalog and can change from one hour to the next. Directories that `unlocks` names can be added after the launch too, so you do not need to wait for them.

## 3. Launch

`"auto"` takes the directories the project qualifies for, up to 100. To choose yourself, send `{"include": [...]}` with the directories to use, or `{"exclude": [...]}` with the ones to skip.

```bash
curl https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/launch \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY" \
  -H "Idempotency-Key: crm_8812-launch" \
  -H "Content-Type: application/json" \
  -d '{"directories": "auto"}'
```

```js
const res = await fetch(
  "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/launch",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}`,
      "Idempotency-Key": "crm_8812-launch",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ directories: "auto" }),
  },
);
console.log(await res.json());
```

```python
import os
import requests

res = requests.post(
    "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/launch",
    headers={
        "Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}",
        "Idempotency-Key": "crm_8812-launch",
    },
    json={"directories": "auto"},
)
print(res.json())
```

Response 202 (some fields left out):

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "status": "in_progress",
  "launched_at": "2026-10-03T14:05:40Z",
  "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 }
}
```

With the `Idempotency-Key`, a launch you send twice after a timeout runs once. A second launch of the same project, with another key, returns 409 `already_launched`.

## 4. Follow the project

The project's `status` and `progress` sum up its listings. Read it, or follow the `project.*` events, to learn about changes.

```bash
curl https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
const res = await fetch("https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", {
  headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` },
});
const project = await res.json();
console.log(project.status, project.progress, project.action);
```

```python
import os
import requests

project = requests.get(
    "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
).json()
print(project["status"], project["progress"], project["action"])
```

Response 200 (some fields left out):

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "status": "action_required",
  "action": {
    "code": "install_badge",
    "waiting_on": "client",
    "client_message": "Please add the directory badges block to https://ledgerly.test. 9 directories list Ledgerly once their badges show on the site."
  },
  "progress": { "total": 96, "pending": 0, "in_progress": 5, "action_required": 9, "submitted": 8, "live": 70, "not_accepted": 3, "cancelled": 1 }
}
```

| `status` | What to do |
| --- | --- |
| `in_progress` | Nothing: listings are moving. |
| `action_required` | Follow `action`: `waiting_on` says whether it is you or your client. |
| `on_hold` | Nothing for `hold_reason` `review`, which clears by itself; write to support for `paused`. |
| `completed` | Nothing is left to run. Listings in `submitted` can still go live. |

## 5. List the listings

Each directory has one listing. Filter by `status` to see the live ones with their links, or the ones that need someone.

```bash
curl "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/listings?status=live&limit=100" \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
let cursor;
const live = [];
do {
  const url = new URL("https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/listings");
  url.search = new URLSearchParams({ status: "live", limit: "100", ...(cursor && { cursor }) });
  const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` } });
  const page = await res.json();
  live.push(...page.data);
  cursor = page.has_more ? page.next_cursor : undefined;
} while (cursor);
console.log(live.map((listing) => [listing.directory.name, listing.live_url]));
```

```python
import os
import requests

live, params = [], {"status": "live", "limit": 100}
while True:
    page = requests.get(
        "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/listings",
        headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
        params=params,
    ).json()
    live += page["data"]
    if not page["has_more"]:
        break
    params["cursor"] = page["next_cursor"]
print([(listing["directory"]["name"], listing["live_url"]) for listing in live])
```

One listing from the response:

```json
{
  "object": "listing",
  "id": "lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK",
  "directory": { "id": "dir_2Lm8Wq3Zk7Rt1Xc5Vb9NdF", "name": "BetaList", "domain_rating": 73 },
  "status": "live",
  "reason": null,
  "action": null,
  "live_url": "https://betalist.com/startups/ledgerly",
  "proof_url": "https://feed.example.net/a/Pf8Kq2Wm.t6Lx9",
  "live_at": "2026-10-08T14:00:00Z"
}
```

A listing in `not_accepted` carries a `reason`: `declined`, `unavailable` or `removed`. Its place in the allowance comes back, and auto-replace or [Reach more directories](https://submitator.com/docs/guides/reach-more-directories) can use it.

Where a directory needed an account, `GET /projects/{project_id}/logins` lists the login created there for your client: where it signs in, its email and username, whether it is ready, and the project's one password. For a confirmation code or a password reset link, `GET /listings/{listing_id}/emails` returns the emails the directory sent to the login, unless they go to your client's own address. See [Logins](https://submitator.com/docs/api/logins).

## 6. Act on what waits for someone

A listing or project in `action_required` has an `action`. The two codes today:

- **`install_badge`** waits on your client. Forward `client_message` as it is, then verify the badge: [Install the badge](https://submitator.com/docs/guides/install-the-badge).
- **`add_assets`** waits on you. Add the missing logo or screenshot, and the listings move on.

New codes can appear. For a code you do not know, show `client_message`, or a general prompt when it is `null`.

## Check it worked

- `GET /projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP` shows a `launched_at` time and a `progress.total` above 0.
- `GET /events?project=prj_4QzX1m9Lr2Vb7Nc8Tk3HwP` starts with `project.launched`.
- `GET /account` shows 1 more launch in `launches.used`.

## What can go wrong

| Code | Status | Fix |
| --- | --- | --- |
| [`not_ready`](https://submitator.com/docs/errors#not_ready) | 422 | The project misses a logo, a screenshot or the contact email; `errors` lists them. Nothing was spent. |
| [`no_launches_left`](https://submitator.com/docs/errors#no_launches_left) | 402 | Your balance is 0. Buy launches in the console, then send the same request again. |
| [`already_launched`](https://submitator.com/docs/errors#already_launched) | 409 | The project launched before. Add directories with `POST /projects/{project_id}/listings` instead. |
| [`nothing_to_submit`](https://submitator.com/docs/errors#nothing_to_submit) | 422 | No directory fits the project now. Check `unlocks` and `not_matching` in the preview. Nothing was spent. |

## Next steps

- [Keep your data in sync](https://submitator.com/docs/guides/keep-your-data-in-sync): follow every change without polling.
- [Reports for clients](https://submitator.com/docs/guides/reports-for-clients): a PDF or XLSX under your agency's name.
- [How it works](https://submitator.com/docs/how-it-works): every status, and the corrections that can follow.
