softon.dev hosts independent data APIs — jobs, events, quotes, and whatever we ship next — behind one account, one key and one response envelope. Read the docs for one of them and you can read the docs for all of them.
Every API on softon.dev ships the same auth, pagination, filtering, error shape and rate-limit headers. New services drop into this grid — and into the docs — without a new mental model for anyone.
Job Data API
Live
Postings from company career pages and boards, deduplicated per source, with the employment type, job function and career level the source itself publishes, plus the full description on a detail fetch.
GET /v1/jobs
GET /v1/jobs/{id}
GET /v1/companies/{id}/jobs
94 postingsRefreshed every 6h · last 1hv1 · stable
Planned: attributed quotations with source, author, era and topic tags, and a deterministic quote-of-the-day endpoint you can cache. Nothing feeds it yet, so the routes below answer 404.
The catalog is a registry, not a hand-built page — a new service registers its name, endpoints and docs tree and appears here. Company data and flight data are in the queue.
Every response is { data, meta, error } with cursor pagination in meta.next and the same five error codes. Write your client once; point it at any dataset. Rate limits and usage roll up to a single account.
Docs that grow themselves
Each service publishes an OpenAPI spec; the portal renders navigation, endpoint pages, field tables and snippets from it. Adding an API adds a docs section — nobody writes the boilerplate twice.
Server-rendered, so it's quick
The site and dashboard are Go templates with HTMX for the interactive parts: search, key management and live request testing return HTML fragments. No client bundle to ship, nothing to hydrate, works with JS off for the parts that matter.
Quickstart
Three minutes to the first row
Sign up, copy the key, call the endpoint. The same snippet works against every dataset in the catalog — swap the path and the filters. No SDK to install: these are the standard library in each language, and they run as they are.
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) thrownew Error(`${res.status} ${await res.text()}`);
const { data, meta } = await res.json();
// meta.next → send it back as ?cursor= for the following page
Plans
Free while you're building.
Free
$0 / month
1,000 requests a month
All live datasets
2 keys, 5 req/s
Community support
Builder
$49 / month
250,000 requests a month
Every dataset, including new ones at launch
10 keys, 50 req/s
Webhooks and CSV export
Scale
Talk to us
Volume pricing per dataset
Bulk snapshots and S3 drops
99.95% SLA, 99.99% option
Shared Slack channel
“Every response is { data, meta, error }. Learn one dataset and you have learned all of them — and when a field is missing from the source, you get null, never a plausible guess.”