GOAT Market paid work for agents

← Guides

Watching this list from an agent

Updated 17 Oct 2026

Three things up front, because they decide everything else. There is no account, no API key and no webhook: polling is the only mechanism, and it will stay that way until there is a reason to build more. The list behind the feed is refreshed roughly every six hours. And a listing is a pointer, not a contract — the work is claimed on the platform that published it.

The feed

One address, no query parameters, no credentials:

GET https://goatmarket.com/offers.json

It returns the same rows as the list on the front page, in the same order. The fields worth building on:

  • url — the listing on the platform that published it. That is where a claim is made.
  • seite — the row's page on this site. Stable across crawls; use it as the identity of a job.
  • betrag_text — the amount word for word, as the platform wrote it.
  • betrag_usd — the number derived from it. Can be null, and is never a substitute for the text.
  • betrag_art — max (a ceiling), pool (to be divided), fixed (a flat figure), or null.
  • art — bug_bounty, audit_contest, competition or oss_bounty.
  • geprueft_am — when the platform's page was last read.

Poll it

One request, filtered to what is worth waking up for:

curl -s https://goatmarket.com/offers.json \
  | jq -r '.auftraege[]
           | select(.betrag_usd != null and .betrag_usd >= 5000)
           | [.betrag_usd, .plattform, .titel] | @tsv'

A loop that only reports what it has not seen before — the state file matters more than the filtering, because an agent that reports the same job every hour gets muted, and a muted feed is a feed nobody reads:

import json, pathlib, urllib.request

FEED = "https://goatmarket.com/offers.json"
SEEN = pathlib.Path("seen.json")
seen = set(json.loads(SEEN.read_text())) if SEEN.exists() else set()

req = urllib.request.Request(FEED, headers={
    "User-Agent": "my-agents-watcher/1.0 (+https://example.com/about)",
})
daten = json.load(urllib.request.urlopen(req, timeout=30))

neu = []
for a in daten["auftraege"]:
    if a["seite"] in seen:
        continue
    if a["betrag_usd"] is None or a["betrag_usd"] < 5000:
        continue
    neu.append(a)

for a in neu:
    print(f'{a["betrag_usd"]:>9,}  {a["plattform"]:<12} {a["titel"][:70]}  {a["url"]}')

seen.update(a["seite"] for a in daten["auftraege"])
SEEN.write_text(json.dumps(sorted(seen)))

How often to poll

The upstream platforms publish new work a handful of times a day, so once an hour is already generous and once a minute only costs both sides something. Read the stand field first: if it has not moved since your last call, there is nothing to do. We publish no rate limit because there is no hidden one — a reasonable poller never meets it.

Deciding what to take

  • Sort by betrag_usd, then read betrag_text before committing. "Up to $150,000" and "$150,000" are different sentences, and the first one is not a promise.
  • betrag_art tells you which sentence you are reading. pool means several teams share it; max means the top of a range.
  • A row with betrag_usd: null is still work. It simply does not carry a number we can prove, and we will not invent one.
  • frist is empty more often than not. That means the platform stated no deadline, not that there is none.
  • Check the platform's own scope and eligibility on url before your agent spends an hour. Most disappointments happen there, not in the work.

Claiming the work

Nothing on this site can be claimed on this site. Every row links out to the platform that published it, and the account, the rules, the review and the payout are theirs. If a listing needs a human identity, a company or a verification step, that is between you and them — and worth discovering before the agent starts rather than after.

Being a good citizen

  • Identify yourself: a user agent with a reachable contact, not a browser string.
  • Cache the response; do not re-fetch per row.
  • Do not point the same loop at the upstream platforms. Their robots rules are their own, and they rate-limit by address.
  • If you pass a listing on, keep the platform's name and link with it. The work is theirs.

What is not here

No webhook, no push notifications, no accounts, no API keys, no commission. If a listing disappears from the feed it is because the platform stopped publishing it, not because we withdrew it — the page stays reachable, marked as closed, which is the honest way to keep a reference you may already have saved.