# Export a market

Turn a saved market into rows: one page, then every page, then the same market as a spreadsheet.

A market is a ranked list of Swiss companies worth selling to, built from a definition you wrote in the app. This page takes one out as data, from the first page to the last, in the two formats the feed answers in.

Open the market and go to its Connect page. It carries a JSON URL, a CSV URL and the button that rotates the token in both.

## Which companies you get

A market sorts its companies into two buckets. **Core** is the market's answer to who to sell to, and it is what the feed serves by default. **Explore** is the wider set the market found and has not committed to, which is where you look when Core is smaller than your team can work through.

```bash
# Core, the default
curl -s 'https://prospex.ch/app/markets/feed/<token>.json?limit=200'

# The wider bucket
curl -s 'https://prospex.ch/app/markets/feed/<token>.json?tab=explore&limit=200'
```

Both buckets carry the same fields, and `bucket` on each row says which one it came from.

## Walking every page

The envelope carries `next_url`, absolute and complete. Following it until it is empty is the entire pagination protocol:

```bash
#!/usr/bin/env bash
set -euo pipefail

url="https://prospex.ch/app/markets/feed/${PROSPEX_MARKET_TOKEN}.json?limit=200"
: > market.jsonl

while [ -n "$url" ]; do
  body=$(curl -sS --fail-with-body "$url") || {
    echo "$body" | jq -r '"refused: \(.error) \(.message)"' >&2
    exit 1
  }
  echo "$body" | jq -c '.data[]' >> market.jsonl
  echo "$body" | jq -r '"\(.count) of \(.total), \(.usage.rows_today) rows today"' >&2
  url=$(echo "$body" | jq -r '.next_url')
done

wc -l market.jsonl
```

Three things to note:

- It never builds a cursor. Cursors belong to the build that issued them; a hand-built one returns 400.
- It reads `.error` from the body of a refusal. Every refusal answers JSON, including the 402 and the 429.
- It watches `usage.rows_today`. A large market walked twice in a day can reach the row budget mid-walk.

## When the market refreshes underneath you

Markets rebuild on a weekly schedule, and a rebuild can reorder the ranking. A cursor issued before it answers:

```json
{
  "error": "invalid_parameter",
  "message": "This market has been refreshed since that cursor was issued, so its order has changed. Start again without a cursor."
}
```

Handle it by restarting the walk from the first page. `market.refreshed_at` in the envelope tells you which build you are reading.

## The same market as a spreadsheet

The CSV needs no loop. It streams the whole bucket in one response, up to 10,000 rows, with human column headers instead of JSON keys:

```bash
curl -sS -o market.csv \
  'https://prospex.ch/app/markets/feed/<token>.csv?tab=core'
```

Lists flatten rather than nest, because a CSV cell has nowhere to put an array: `Reasons` joins with a pipe, `Missing` with a comma, and the signal columns carry titles and ids for the same few recent events. When you need the structure, read the JSON. [The market feed](https://prospex.ch/docs/market-feed/) lists every column and the key behind it.

## The four fields to map first

A market row carries the registry facts you would expect. These are the ones that carry the market's own judgement:

| Field | Use it for |
| --- | --- |
| fit | Sorting and filtering. It is the stored word, so it survives a change of interface language; `fit_label` is the version to show a person. |
| reasons | The first line of an opener, and the answer to "why is this company on my list". Same words the workspace shows. |
| missing | Deciding what to enrich yourself. An empty field means Prospex does not hold the data. |
| in_crm | Skipping companies your CRM already holds, if you connected one. |
