Docs · Main track · API reference

DocsMain track

API reference

Every endpoint a Main-track team can call, with its request, response and errors.

View as MarkdownThis page as plain text
llms.txtIndex of every docs page, for agents

Download problems and submit from a script. The starter repository uses these same endpoints. To give this reference to an agent, use Copy page or the Markdown version from the menu next to the title.

Basics

Base URLhttps://frontieror-challenge.com/api/v1
AuthenticationAuthorization: Bearer fo_… with your personal API token. See Authentication.
FormatJSON with snake_case fields. Times are ISO 8601 in UTC.
Errors{"error": "…"}, with a message you can show as it is.
LimitsRequest bodies up to 4 MB. Submissions count toward your team's daily limit: one per UTC day, 4 during the testing stage.

The examples use httpx, which the starter uses too. Create the client once:

client.py
import os

import httpx

api = httpx.Client(
    base_url="https://frontieror-challenge.com/api/v1",
    headers={"Authorization": f"Bearer {os.environ['FRONTIEROR_TOKEN']}"},
    timeout=60,
)

Errors every endpoint can return

StatusMessage
401Missing or invalid API token.
403Join or create a team first. (Only endpoints that need a team.)
500Something went wrong, try again.

Get your account

GET/meToken

Who the token belongs to, their team, the current stage and round, and the submissions left today. Use it to check a token.

Responses

200OKapplication/json
  • userobject
    • idstring
    • namestring
    • emailstring
  • teamobject · null

    null until you join or create a team.

    • idstring
    • namestring
    • trackenum

      main or self-hosted

  • stageobject · null

    The running stage, else the next one, else the last one.

    • idstring
    • namestring
    • statusenum

      upcoming, active or ended.

    • start_datestring · date-time
    • end_datestring · date-time
  • roundobject · null

    The current round of the stage: the open one, else the next one, else the last one. null if the stage has no rounds.

    • numberinteger

      1-based.

    • ofinteger

      How many rounds the stage has.

    • statusenum

      upcoming, open or closed. You can submit only while it is open.

    • start_datestring · date-time
    • end_datestring · date-time
    • self_hosted_window_sinteger · null

      How long a Self-hosted submission in this round stays open, in seconds. See Submission flow. null until the round opens.

  • submissions_left_todayinteger · null

    Resets at 00:00 UTC. null without a team or a stage.

RequestPython
me = api.get("/me").json()
print(me["team"], me["submissions_left_today"])
Response
{
  "user": { "id": "5b0f…", "name": "Ada Lovelace", "email": "ada@example.com" },
  "team": { "id": "c2a1…", "name": "Column Generators", "track": "main" },
  "stage": {
    "id": "91de…",
    "name": "Stage 1",
    "status": "active",
    "start_date": "2027-01-04T05:00:00.000Z",
    "end_date": "2027-02-01T05:00:00.000Z"
  },
  "round": {
    "number": 2,
    "of": 4,
    "status": "open",
    "start_date": "2027-01-11T05:00:00.000Z",
    "end_date": "2027-01-18T05:00:00.000Z",
    "self_hosted_window_s": 5100
  },
  "submissions_left_today": 3
}

List problems

GET/problemsToken

The problems of the current round, once it has opened: the ones a submission covers. Their slugs are the names your solvers go by.

Query parameters

  • detailsflagoptional

    Add ?details=1 to include each problem's description, schemas and checker, as in Get a problem.

Responses

200OKapplication/json

An array of problems, empty before the round opens.

  • slugstring
  • namestring
  • categoryenum

    The leaderboard category: routing, production_inventory, scheduling, packing_partitioning, graph_quadratic or network_design_location

  • formulation_typeenum

    The leaderboard formulation: combinatorial, mixed_integer, quadratic, stochastic or robust_bilevel

  • objectiveenum

    minimize or maximize

RequestPython
problems = api.get("/problems").json()
slugs = [p["slug"] for p in problems]
200 OKResponse
[
  {
    "slug": "barnhart2000",
    "name": "Origin-Destination Integer Multicommodity Flow (ODIMCF)",
    "category": "network_design_location",
    "formulation_type": "combinatorial",
    "objective": "minimize"
  }
]

Get a problem

GET/problems/{slug}Token

One problem of the current stage with its statement and schemas: the content of its problem page.

Path parameters

  • slugstringrequired

Responses

200OKapplication/json
  • slugstring
  • namestring
  • categoryenum

    The leaderboard category: routing, production_inventory, scheduling, packing_partitioning, graph_quadratic or network_design_location

  • formulation_typeenum

    The leaderboard formulation: combinatorial, mixed_integer, quadratic, stochastic or robust_bilevel

  • objectiveenum

    minimize or maximize

  • descriptionstring

    The statement in Markdown, with TeX math.

  • instance_schemaobject

    An annotated example of an instance file. See Reading the schemas.

  • solution_schemaobject

    An annotated example of a solution file.

  • solution_json_schemaobject

    The solution schema as a JSON Schema, to validate solutions with any JSON Schema library. It checks the format only; feasibility is up to the checker.

  • checkerstring · null

    The source of the checker our evaluation runs, feasibility_check.py. null when the problem's checker is not published. See Checkers.

404Not found

No problem with this slug in the current stage.

RequestPython
problem = api.get("/problems/barnhart2000").json()
print(problem["solution_schema"])
Response
{
  "slug": "barnhart2000",
  "name": "Origin-Destination Integer Multicommodity Flow (ODIMCF)",
  "category": "network_design_location",
  "formulation_type": "combinatorial",
  "objective": "minimize",
  "description": "…",
  "instance_schema": { "network": { … }, "commodities": { … } },
  "solution_schema": {
    "objective_value": "<float> Total cost of rejected commodities across the entire routing plan.",
    "commodities": [ … ]
  },
  "checker": "…"
}

List public instances

GET/problems/{slug}/instancesToken

The problem's public instances, for development. Private instances are never listed.

Path parameters

  • slugstringrequired

Responses

200OKapplication/json

An array of instances, ordered by name.

  • idstring · uuid
  • namestring
  • time_limit_sinteger

    Seconds a solver gets for this instance.

404Not found

No problem with this slug in the current stage.

RequestPython
instances = api.get("/problems/barnhart2000/instances").json()
200 OKResponse
[
  { "id": "0f1e8c2a-…", "name": "tiny_instance", "time_limit_s": 60 }
]

Download a public instance

GET/problems/{slug}/instances/{id}Token

The instance file itself, in the problem's instance schema, sent as an attachment named <slug>-<name>.json.

Path parameters

Responses

200OKapplication/json

The instance file.

404Not found

This problem has no public instance with this id.

RequestPython
from pathlib import Path

for instance in instances:
    data = api.get(f"/problems/barnhart2000/instances/{instance['id']}").content
    Path(f"{instance['name']}.json").write_bytes(data)

List images

GET/imagesToken

Our images with their packages and full lock files, and the limits on extra packages. See Environment.

Responses

200OKapplication/json
  • default_imagestring

    Used when a submission names no image: python-3.12.

  • imagesobject[]
    • idstring

      The value for docker_image, such as python-3.14.

    • labelstring
    • python_versionstring
    • pullstring · null

      The image to docker pull, to run solvers locally in the same environment.

    • packagesstring[]

      The packages installed on purpose.

    • lockstring

      Every installed package with its exact version, in requirements format.

  • requirementsobject
    • max_packagesinteger
      50
    • max_bytesinteger

      10240

RequestPython
images = api.get("/images").json()
for image in images["images"]:
    print(image["id"], image["pull"])
200 OKResponse
{
  "default_image": "python-3.12",
  "images": [
    {
      "id": "python-3.14",
      "label": "Python 3.14",
      "python_version": "3.14",
      "pull": "…:3.14",
      "packages": ["numpy", "scipy", "pandas", "networkx",
                   "ortools", "pulp", "highspy", "pyomo"],
      "lock": "absl-py==2.5.0\nannotated-types==0.8.0\n…"
    }
  ],
  "requirements": { "max_packages": 50, "max_bytes": 10240 }
}

Submit an archive

POST/main/submissionsToken · Main team

Checks the archive straight away, as the dashboard does, and queues it for evaluation. A refused upload does not use one of the day's submissions. Then poll Get a submission.

Bodymultipart/form-data

  • filebinaryrequired

    The archive: zip, tar or tar.gz. The whole body is at most 4 MB. See Submission format.

  • docker_imagestringoptional

    python-3.12 (default) or python-3.14.

  • requirementsstring · binaryoptional

    Extra packages, one per line, as text or as a file. Only with our images.

A body that is not multipart is taken as the archive itself, run in python-3.12 without extra packages.

Query parameters

  • acknowledge_missingflagoptional

    Required when the archive has no folder for some problems. Those problems score 0.

Responses

201Createdapplication/json
  • idstring · uuid
  • statusenum

    Always pending here.

  • missing_problemsstring[]

    Slugs of the problems without a folder.

  • docker_imagestring

    The image it runs in.

  • progressobject

    Where it is in the queue and when its results should arrive. See Get a submission.

400Bad request

The body is empty or cannot be read, or the form has no file part.

403Forbidden

You have no team, or your team is on the Self-hosted track.

409Conflict

The submission was refused, and the message says why:

  • no round is open;
  • the file is not a zip or tar archive, is damaged, or holds a link or an unsafe path;
  • no problem has a solver, or some are missing without acknowledge_missing=1;
  • the image or the extra packages are not valid.
413Payload too large

The body is larger than 4 MB.

429Too many requests

Your team has used today's submissions: one per UTC day, 4 during the testing stage. You can submit again after 00:00 UTC.

RequestPython
with open("submission.zip", "rb") as f:
    r = api.post(
        "/main/submissions",
        files={"file": ("submission.zip", f, "application/zip")},
        data={"docker_image": "python-3.14",
              "requirements": "pyscipopt==5.5.0"},
    )
if r.is_error:
    raise SystemExit(r.json()["error"])
submission_id = r.json()["id"]
Response
{
  "id": "3f6c2d1e-8a4b-4c0e-9f7a-2b5d8e1c4a90",
  "status": "pending",
  "missing_problems": [],
  "docker_image": "python-3.14",
  "progress": {
    "ahead": 2,
    "instances_done": null,
    "instances_total": null,
    "results_in_minutes": 14
  }
}

Get a submission

GET/submissions/{id}Token · team member

One of your team's submissions with its status and results. Poll it every few seconds until status is completed or failed.

Path parameters

  • idstring · uuidrequired

Responses

200OKapplication/json
  • idstring · uuid
  • trackenum

    main

  • statusenum

    pending (queued), running, completed or failed. See Statuses.

  • scorenumber · null

    The submission's score once completed.

  • errorstring · null

    Why a failed submission could not be evaluated at all.

  • created_atstring · date-time
  • updated_atstring · date-time
  • progressobject · null

    While the submission is pending or running, where it is and when its results should arrive; null otherwise.

    • aheadinteger

      Submissions queued before this one.

    • instances_doneinteger · null

      Instances evaluated so far, once evaluation has started.

    • instances_totalinteger · null

      Instances to evaluate, once evaluation has started.

    • results_in_minutesinteger

      Minutes until the results should arrive, rounded up. Every instance is counted at its full time limit, so results usually come sooner.

  • environmentobject
    • imagestring
    • requirementsstring · null
    • statusenum · null

      building, ready or failed when you listed extra packages.

    • installedstring · null

      The exact installed versions, once ready.

    • build_logstring · null

      Why the install failed.

  • problemsobject[]
    • slugstring
    • namestring
    • scorenumber · null

      The problem's score: the mean over its private instances.

    • instancesobject[]
      • namestring
      • setenum

        public or private. Public instances run first, as a check, and don't count towards the score.

      • scorenumber · null
      • errorstring · null

        Why the instance scored 0, such as “Time limit exceeded (60 s)”. null for a feasible solution. See what can happen to an instance.

404Not found

Your team has no submission with this id.

RequestPython
import time

while True:
    s = api.get(f"/submissions/{submission_id}").json()
    if s["status"] in ("completed", "failed"):
        break
    if s["progress"]:
        print(f"{s['status']}: results in about {s['progress']['results_in_minutes']} min")
    time.sleep(10)
print(s["score"], s["error"])
Response
{
  "id": "3f6c2d1e-8a4b-4c0e-9f7a-2b5d8e1c4a90",
  "track": "main",
  "status": "completed",
  "score": 0.8123,
  "error": null,
  "created_at": "2027-01-13T09:14:03.512Z",
  "updated_at": "2027-01-13T09:31:47.020Z",
  "progress": null,
  "environment": {
    "image": "python-3.14",
    "requirements": "pyscipopt==5.5.0",
    "status": "ready",
    "installed": "absl-py==2.5.0\n…",
    "build_log": null
  },
  "problems": [
    {
      "slug": "barnhart2000",
      "name": "Origin-Destination Integer Multicommodity Flow (ODIMCF)",
      "score": 0.8123,
      "instances": [
        { "name": "large_instance_1", "set": "private",
          "score": 0.8123, "error": null },
        { "name": "tiny_instance", "set": "public",
          "score": 1.0, "error": null }
      ]
    }
  ]
}