prospex
Prospex docs

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.

# 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:

#!/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:

{
  "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:

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 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:

FieldUse 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.

Cookie preferences

Necessary cookies always run. The other two are on unless you turn them off.