Sports & Fitness — entry 028 of 34
Squiggle
Squiggle is a free JSON API for AFL fixtures, results, ladders, and its signature aggregated margin predictions pooled from multiple tipping models. Its own docs require setting a descriptive `User-Agent` containing a contact email rather than an API key, and since v1.11.0 the API actively refuses requests carrying common scraper user-agent strings, so `auth: None -> User-Agent` is corrected. A live Origin-header probe of the real host returned an explicit `{"error":"forbidden_origin"}` rejection for a cross-origin browser request -- reachable directly, but not CORS-open to arbitrary origins -- correcting `cors: yes -> no`.
Squiggle is a single-endpoint JSON API for AFL fixtures, results, ladders, and its signature aggregated margin predictions: every call hits the same root path with a `q=` parameter selecting the resource (`teams`, `games`, `tips`, `ladder`, and more). No API key is required, but the API actively rejects requests that don't self-identify with a descriptive `User-Agent`, and it refuses cross-origin browser calls outright regardless of UA.
GreatAPIs Score
Auth quickstart
- No API key or signup, but every request must carry a descriptive `User-Agent` naming your app and a contact method. A live GET this run with the default `curl/7.88.1` UA still returned HTTP 403, and the response body nests the rejection inside the requested resource's own key: `{"teams":[{"api_version":"1.13.2","your_user_agent":"curl/7.88.1","error":"bad_UA","warning":"Your UserAgent has not been set correctly. To avoid bans, please see https://api.squiggle.com.au/#section_bots"}]}` -- swapping in a UA like `greatapis.com quickstart (contact: hello@greatapis.com)` returns the real data instead, confirmed live this run.
Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.
List AFL teams
GEThttps://api.squiggle.com.au/?q=teams
User-Agent: greatapis.com quickstart (contact: hello@greatapis.com)
{"teams":[{"retirement":9999,"logo":"/wp-content/themes/squiggle/assets/images/Adelaide.png","abbrev":"ADE","id":1,"debut":1991,"name":"Adelaide"},{"retirement":9999,"logo":"/wp-content/themes/squiggle/assets/images/Brisbane.png","abbrev":"BRI","id":2,"debut":1987,"name":"Brisbane Lions"},{"...":"16 more teams, same shape"}]}Try it
Developer reference
https://api.squiggle.com.au- GET/?q=teams
- GET/?q=games&year={year}&round={round}
- GET/?q=tips&year={year}&round={round}
Gotchas & limits
- A rejected request due to a bad User-Agent is HTTP 403, but the error payload is nested inside the requested resource's own array key (`teams`/`games`/etc.), not a top-level `error` field -- see the authSteps transcript above; a naive client that only checks `response.ok` before reading `data.teams[0].name` will silently render the error object as if it were a team.
- Cross-origin browser calls are refused outright, independent of the User-Agent check: a live GET with a correct UA plus `Origin: https://greatapis.com` this run still returned HTTP 403 with `{"teams":[{"api_version":"1.13.2","message":"Cross-origin browser access from this origin is not allowed.","error":"forbidden_origin"}]}` -- server-side or same-origin calls only, no CORS workaround exists client-side.
- `q=tips` returns one row per tipster source per game, not one row per game: a live `GET /?q=tips&year=2024&round=1` this run returned multiple entries sharing the same `gameid` (e.g. `35704`), each from a different `source` (`"Squiggle"`, `"Matter of Stats"`, `"Punters"`, ...) with its own `margin`/`confidence` -- summing or averaging without grouping by `gameid` double-counts games.
- The documented query syntax uses semicolons between parameters (`?q=games;year=2024;round=1`), but a live test this run confirmed standard `&`-separated query strings (`?q=games&year=2024&round=1`) return byte-identical results -- either separator works against the real host.