Developers

a rest api over the same thing the app is: scoring systems you write yourself, leagues run under them, and the boards that come out. built for ai tools and external apps, so every answer is json and every id you get back is an id you can send.

v1.0.0 · https://sicko-fantasy.com/api/v1

Auth

one header. make a key on your account page, copy it when it is shown (it is shown once, and is not stored), and send it as a bearer token.

curl https://sicko-fantasy.com/api/v1/me \
  -H "Authorization: Bearer sk_sicko_..."

a key acts as the account that made it and can do exactly what that account can do: its own systems, its own leagues, the teams it runs. a key is never an admin, so a league you are not in stays readable and unwritable. revoke one from the same page and it stops working on the next request.

Two tiers

freerun the ecosystem

everything for building and running your own leagues: create scoring systems, create leagues, read standings and matchups, set lineups, add and drop, file waiver claims, propose and answer trades, import from sleeper and sync.

60 requests a minute per key

proanalysis and optimized lineups

the expensive half: ranked boards with points above average starter, balance scorecards, two systems compared player by player, bench points, and the lineup optimizer for any set of players under any rules. these score a whole season on every call, which is why they are the ones behind a plan.

600 requests a minute per key

pro is granted by hand right now, on the account rather than the key. if you are building something and want it, say so. a free key on a pro endpoint gets 402 with {"error":"pro_plan_required"}, not a silent empty answer.

Rate limits

a fixed window of 60 seconds, counted per key. every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so you can pace from any response rather than only from the ones that worked. over the line is a 429 with Retry-After in whole seconds.

Errors

every failure is the same two fields. error is a stable code to branch on; message is the sentence a person reads, and it is the league's own wording, so a refused signing says which team already has him.

{ "error": "conflict", "message": "Kyler Murray is already rostered by Team 3" }

Endpoints

paths are relative to https://sicko-fantasy.com/api/v1. the machine-readable version is openapi.json, which needs no key and is what to point a tool at.

meta

the key, the plan, and the spec.

  • GET/openapi.jsonno key

    This document.

  • GET/mefree

    Who this key is, what plan it is on, and how much of the minute is left.

scoring systems

the rules a season is scored under.

  • GET/systemsfree

    The scoring systems this key can see: the builtin templates plus your own.

  • POST/systemsfree

    Create a scoring system from a rules object.

  • GET/systems/{id}free

    One scoring system, with its rules. Reading any system is open.

  • PATCH/systems/{id}free

    Rename, redescribe or re-rule a system you own. Builtins refuse an edit from everyone.

  • DELETE/systems/{id}free

    Delete a system you own. A system a league is playing under cannot be deleted.

players

players and team defenses, optionally scored.

  • GET/playersfree

    Search players and team defenses, optionally scored under a system.

leagues

leagues, standings, matchups, sleeper import.

  • GET/leaguesfree

    The leagues this key is in: the ones it created, and the ones it runs a team in.

  • POST/leaguesfree

    Create a league, its teams and a regular-season schedule.

  • POST/leagues/import/sleeperfree

    Import a Sleeper league, or preview what an import would create.

  • GET/leagues/{id}free

    A league, its teams, its standings and the week it is on.

  • GET/leagues/{id}/matchupsfree

    A league's scored games. A bye has a null away side and no winner.

  • POST/leagues/{id}/syncfree

    Pull a linked league back into line with Sleeper.

teams

rosters and lineups.

  • GET/leagues/{id}/teams/{teamId}free

    A team's roster and its lineup for a week.

  • PUT/leagues/{id}/teams/{teamId}/lineupfree

    Set who starts: the whole card, or one slot at a time.

  • POST/leagues/{id}/teams/{teamId}/lineup/bestfree

    Start the best legal lineup for a week, by what those players actually scored.

  • POST/leagues/{id}/teams/{teamId}/rosterfree

    Sign a free agent, drop a player, or swap one for another.

waivers

claims, filed and settled.

  • GET/leagues/{id}/teams/{teamId}/waiversfree

    This team's waiver claims, newest first.

  • POST/leagues/{id}/teams/{teamId}/waiversfree

    File a waiver claim. The league has to be on waivers.

  • GET/leagues/{id}/waiversfree

    Every waiver claim in a league, newest first. The transactions list, as data.

  • DELETE/leagues/{id}/waivers/{claimId}free

    Withdraw a pending claim. It is cancelled, not deleted.

trades

offers between two teams.

  • GET/leagues/{id}/tradesfree

    A league's trades, newest first.

  • POST/leagues/{id}/tradesfree

    Offer a trade. Both sides must be non-empty.

  • GET/leagues/{id}/trades/{tradeId}free

    One trade.

  • POST/leagues/{id}/trades/{tradeId}free

    Accept, reject or cancel a trade.

analysis

rankings, balance, comparison, the optimizer.

  • GET/leagues/{id}/teams/{teamId}/bench-pointspro

    What the bench was worth: actual against the best legal lineup of the same roster. Pro.

  • GET/analysis/rankingspro

    A ranked board under any scoring system, with positional value. Pro.

  • GET/analysis/balancepro

    Is a scoring system balanced across positions? Pro.

  • GET/analysis/comparepro

    The same players ranked under two scoring systems. Pro.

  • POST/optimizepro

    The best legal lineup for a set of players. Pro.

Worked examples

write a scoring system

a system is data: weights per stat, bracket tiers, per-position overrides. the whole catalog of scoreable stats is on the systems page.

curl -X POST https://sicko-fantasy.com/api/v1/systems \
  -H "Authorization: Bearer $SICKO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "coverage db",
        "rules": {
          "global": { "weights": { "rec_yards": 0.1, "rec_tds": 6 } },
          "positionOverrides": {
            "DB": { "weights": { "cov_yards": -0.25, "def_interceptions": 6 } }
          }
        }
      }'

start a league and set a lineup

curl -X POST https://sicko-fantasy.com/api/v1/leagues \
  -H "Authorization: Bearer $SICKO_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "sicko test", "season": 2025, "scoringSystemId": "db-team-defense",
        "teamCount": 10, "acquisitionMode": "waivers" }'

# the whole card at once, one entry per slot, null for an empty one
curl -X PUT "https://sicko-fantasy.com/api/v1/leagues/LEAGUE/teams/TEAM/lineup?week=3" \
  -H "Authorization: Bearer $SICKO_KEY" -H "Content-Type: application/json" \
  -d '{ "slots": ["00-0036389", null, "DST_SF"] }'

optimize a lineup pro

name a league team and it takes that roster, those rules, that season and those slots. or send your own players, your own rules and your own slots and optimize anything at all. the answer is exact, not greedy.

curl -X POST https://sicko-fantasy.com/api/v1/optimize \
  -H "Authorization: Bearer $SICKO_KEY" -H "Content-Type: application/json" \
  -d '{ "leagueId": "sicko-test", "teamId": "TEAM", "week": 7 }'

# ...or anything at all
curl -X POST https://sicko-fantasy.com/api/v1/optimize \
  -H "Authorization: Bearer $SICKO_KEY" -H "Content-Type: application/json" \
  -d '{ "systemId": "db-team-defense", "season": 2025, "week": 7,
        "players": ["00-0036389", "DST_SF"],
        "slots": { "slots": [{ "slot": "FLEX", "eligible": ["RB","WR","TE"] }], "bench": 4 } }'

read a board pro

every row carries points above average starter, which is the only honest way to compare a tight end to a linebacker: a player's ppg minus the startable level at their own position.

curl "https://sicko-fantasy.com/api/v1/analysis/rankings?system=db-team-defense&season=2025&pos=DB&sort=paa" \
  -H "Authorization: Bearer $SICKO_KEY"

Notes worth knowing

  • ids are entity ids. a player is an nflverse gsis id like 00-0036389; a team defense is DST_SF. both work everywhere a player id is taken.
  • lineups lock. in a league playing the season being played now, a player whose game has kicked off cannot be moved into or out of a lineup, and cannot be dropped. that is a 409 with "error": "locked".
  • a league imported from sleeper is a mirror. its rosters are re-read every night, so adds, claims and trades are refused in it. make the move on sleeper and sync.
  • scores are hindsight, not projection. the optimizer values players by what they actually scored that week under your rules. hand the same solver projections and nothing about the answer changes.