Docs / Job Data API / Endpoints / List jobs
List jobs
Returns postings newest first. Every filter is optional and they combine with AND. An unrecognised parameter is rejected with a 400 rather than silently ignored, so a typo in a filter name cannot look like an empty result set.
Query parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Full-text match on title, company and description. |
remote |
boolean | Restrict to remote-eligible postings. |
company |
string | Company slug or name. |
posted_after |
date | ISO-8601 date. Filters on posted_at -- the date the SOURCE published the posting, not the date we first saw it. Page by order=ingested_at for the latter. |
limit |
integer | Items per page, 1–100. Defaults to 25. |
cursor |
string | Opaque cursor from a previous response's meta.next. Do not construct one. |
order |
string | Sort field, prefixed with - to reverse. |
source |
string | Restrict to one scraper source. |
Request
curl -G https://api.softon.dev/v1/jobs \
-H "Authorization: Bearer $SOFTON_KEY" \
-d q=golang -d remote=true -d posted_after=2026-08-01
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.softon.dev/v1/jobs", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("SOFTON_KEY")) q := req.URL.Query() q.Set("q", "golang") q.Set("remote", "true") q.Set("posted_after", "2026-08-01") req.URL.RawQuery = q.Encode() res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() var page struct { Data []Job `json:"data"` Meta struct{ Next *string `json:"next"` } `json:"meta"` } if err := json.NewDecoder(res.Body).Decode(&page); err != nil { log.Fatal(err) } // page.Meta.Next → send it back as ?cursor= for the following page
import json, os, urllib.parse, urllib.request url = "https://api.softon.dev/v1/jobs" + "?" + urllib.parse.urlencode({ "q": "golang", "remote": "true", "posted_after": "2026-08-01", }) req = urllib.request.Request(url, headers={ "Authorization": "Bearer " + os.environ["SOFTON_KEY"], }) page = json.load(urllib.request.urlopen(req)) # page["meta"]["next"] → send it back as ?cursor= for the following page
const url = new URL("https://api.softon.dev/v1/jobs"); url.searchParams.set("q", "golang"); url.searchParams.set("remote", "true"); url.searchParams.set("posted_after", "2026-08-01"); const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SOFTON_KEY}` }, }); if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); const { data, meta } = await res.json(); // meta.next → send it back as ?cursor= for the following page
Response
The envelope is identical on every softon.dev API: data, meta, error. Only the shape inside data changes per dataset — see the response envelope.
{
"data": [
{
"id": "glorri:fincaazerbaijan-neftcala-filiali-uzre-filial-kredit-satis-iscisi-72665882",
"source": "glorri",
"title": "Neftçala filialı üzrə Filial Kredit Satış İşçisi",
"company": { "name": "FINCA Azerbaijan", "slug": "fincaazerbaijan" },
"location": "Neftçala, Azərbaycan",
"remote": false,
"employment_type": "Full-time",
"job_function": "Bank services",
"career_level": "Professional",
"posted_at": "2026-08-04T13:49:26.33Z",
"url": "https://jobs.glorri.com/en/vacancies/fincaazerbaijan/fincaazerbaijan-neftcala-filiali-uzre-filial-kredit-satis-iscisi-72665882",
"description": null,
"requirements": null,
"ingested_at": "2026-08-04T23:55:43.722474Z"
}
],
"meta": { "count": 1, "next": "eyJvIjoyfQ", "request_id": "req_9Fv3" },
"error": null
}
Fields of each item in data[]
| Field | Type | Description |
|---|---|---|
id |
string | Stable identifier, "<source>:<source id>". Pass it to /v1/jobs/{id}. |
source |
string | Which scraper produced this posting. |
title |
string | Posting title as published. |
company |
object · nullable | The hiring company: name and slug. null when the source omits it. |
location |
string · nullable | Free-text location as published. |
remote |
boolean · nullable | Whether the posting is remote-eligible. |
employment_type |
string · nullable | Full-time, Part-time, Contract, as the source labels it. |
job_function |
string · nullable | The source's own function taxonomy, not normalised across sources. |
career_level |
string · nullable | The source's own seniority label. Not normalised, so it is not called "seniority". |
posted_at |
timestamp · nullable | When the posting first appeared, RFC 3339. |
url |
string · nullable | Canonical page on the source site. |
description |
string · nullable | Full posting body. Present when the crawler has fetched the posting's own page, null otherwise — on collections and on /v1/jobs/{id} alike, since neither fetches on demand. |
requirements |
string · nullable | Requirements section when the source separates it, subject to the same condition as description. |
facts |
object · nullable | The source's own labelled metadata, e.g. "Apply Before". Label set differs per source. |
ingested_at |
timestamp | When this version was stored. |
Try it
Send the request to see a response.