Docs


API examples

Copy-paste recipes for the common API tasks, each with the curl request and its Python SDK equivalent side by side.

Last updated June 7, 2026

Worked recipes for the tasks you will reach for most. Each one shows the curl request and its Python SDK equivalent in tabs, so pick whichever fits your stack. Every example uses the base URL from API overview. The wider Python set lives in End-to-end examples.

Set your key once

The curl snippets read your key from an environment variable so you never paste it inline. Create a key on Creating and managing API keys, then export it:

export VILVIK_KEY="vlk_your_key_here"

The Python SDK reads its key from VILVIK_API_KEY instead, so vilvik.Client() picks it up with no argument. See Installation and authentication.

Create a submission

Send the same parameters you would set in the website's new-submission form. The fitness_func is a Python source string, and fitness_func_entry names the top-level symbol in that source the runtime should call. The website fills the entry in for you as you type; over the API you set it yourself. Every code field works this way: send <field> with the source and <field>_entry with the name to call (for example on_generation and on_generation_entry).

curl -X POST https://beta.vilvik.com/api/v1/submissions/ \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "from script",
    "num_generations": 100,
    "num_parents_mating": 4,
    "sol_per_pop": 10,
    "num_genes": 5,
    "init_range_low": -2,
    "init_range_high": 2,
    "fitness_func": "def fitness_func(ga, sol, idx):\n    return sum(sol)\n",
    "fitness_func_entry": "fitness_func"
  }'
import vilvik

client = vilvik.Client()

submission = client.submissions.create(
    name="from script",
    num_generations=100,
    num_parents_mating=4,
    sol_per_pop=10,
    num_genes=5,
    init_range_low=-2,
    init_range_high=2,
    fitness_func=(
        "def fitness_func(ga, sol, idx):\n"
        "    return sum(sol)\n"
    ),
    fitness_func_entry="fitness_func",
)
print(submission.id, submission.status_url)

The reply is 202 Accepted with the new id and the URLs you poll:

{
  "id": "abcDEF123456",
  "status": "queued",
  "status_url": "https://beta.vilvik.com/api/v1/submissions/abcDEF123456",
  "result_url": "https://beta.vilvik.com/api/v1/results/?submission_id=abcDEF123456"
}

The optional Idempotency-Key header lets you retry the same request without creating a duplicate. The SDK sets one for you. Scope: submissions:write.

Create a quick submission

Quick submissions bring their own fitness function and parameter defaults, so you set submission_type and pass the problem's inputs instead of writing fitness_func.

The example below asks the service to find a subset of eight integers that sums to 20:

curl -X POST https://beta.vilvik.com/api/v1/submissions/ \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "subset sum",
    "submission_type": "quick_binary_subset_sum",
    "integers": [3, 7, 1, 9, 4, 2, 8, 5],
    "target": 20
  }'
import vilvik

client = vilvik.Client()

submission = client.submissions.create(
    name="subset sum",
    submission_type="quick_binary_subset_sum",
    integers=[3, 7, 1, 9, 4, 2, 8, 5],
    target=20,
)
print(submission.id, submission.status)

The reply is the same 202 Accepted shape as above. The generated object in the response echoes the data the run actually used. Here it confirms the integers (useful when you send num_integers instead of integers and let the service pick them, so the run is still reproducible from the response alone):

{
  "id": "abcDEF123456",
  "status": "queued",
  "generated": {
    "integers": [3, 7, 1, 9, 4, 2, 8, 5]
  },
  ...
}

Do not send fitness_func (or any other code field) with a quick type. The request is rejected because these submissions use built-in code.

Scope: submissions:write. For the full list of quick types and their inputs, see Quick submission types.

Create, then wait for the result

Submit a run, then block until it leaves the queued or running state and fetch the result. The curl version polls the status URL in a loop (needs jq); the SDK does the loop for you with wait_for.

id=$(curl -s -X POST https://beta.vilvik.com/api/v1/submissions/ \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"num_generations":50,"sol_per_pop":10,"num_genes":5,"num_parents_mating":4,"init_range_low":-2,"init_range_high":2,"fitness_func":"def fitness_func(ga, sol, idx):\n    return sum(sol)\n","fitness_func_entry":"fitness_func"}' \
  | jq -r .id)

while :; do
  status=$(curl -s -H "Authorization: Bearer $VILVIK_KEY" \
    "https://beta.vilvik.com/api/v1/submissions/$id" | jq -r .status)
  echo "status: $status"
  case "$status" in queued|running) sleep 3;; *) break;; esac
done

curl -s -H "Authorization: Bearer $VILVIK_KEY" \
  "https://beta.vilvik.com/api/v1/results/?submission_id=$id" | jq
import vilvik

client = vilvik.Client()

submission = client.submissions.create(
    num_generations=50,
    sol_per_pop=10,
    num_genes=5,
    num_parents_mating=4,
    init_range_low=-2,
    init_range_high=2,
    fitness_func=(
        "def fitness_func(ga, sol, idx):\n"
        "    return sum(sol)\n"
    ),
    fitness_func_entry="fitness_func",
)

result = client.results.wait_for(submission.id, timeout=900)
print("best fitness:", result.best_fitness)
print("best solution:", result.best_solution)

For a push notification instead of polling, register a webhook (see below).

List your submissions

curl -H "Authorization: Bearer $VILVIK_KEY" \
     "https://beta.vilvik.com/api/v1/submissions/?page_size=20"
import vilvik

client = vilvik.Client()

page = client.submissions.list(limit=20)
for sub in page:
    print(sub.id, sub.status, sub.name)

Returns a cursor-paginated page. Filtering by status, date, name, and more is in Filtering submissions and results. Scope: submissions:read.

Page through every submission

Follow the cursor until it runs out. The SDK's iter_all generator does this for you.

cursor=""
while :; do
  page=$(curl -s -H "Authorization: Bearer $VILVIK_KEY" \
    "https://beta.vilvik.com/api/v1/submissions/?page_size=100$cursor")
  echo "$page" | jq -r '.results[] | "\(.created_at)  \(.id)  \(.status)  \(.name)"'
  cursor=$(echo "$page" | jq -r '.next // empty')
  [ -z "$cursor" ] && break
  cursor="&${cursor#\?}"
done
import vilvik

client = vilvik.Client()

for sub in client.submissions.iter_all(limit=100):
    print(sub.created_at, sub.id, sub.status, sub.name)

Fetch a single submission

curl -H "Authorization: Bearer $VILVIK_KEY" \
     "https://beta.vilvik.com/api/v1/submissions/abcDEF123456"
import vilvik

client = vilvik.Client()

submission = client.submissions.get("abcDEF123456")
print(submission.status)

Returns the full row, including current status and timing. Scope: submissions:read.

Re-execute a submission

Run the same parameters again as a fresh job. Useful when a run had random behaviour and you want another sample.

curl -X POST https://beta.vilvik.com/api/v1/submissions/abcDEF123456/reexecute \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import vilvik

client = vilvik.Client()

rerun = client.submissions.reexecute("abcDEF123456")
print(rerun.id, rerun.status_url)

You may pass an optional JSON body to change name, webhook_url, or notification_email for the new run. Scope: submissions:write.

Cancel or delete a submission

A single DELETE removes the submission. If it is still queued or running, the worker is stopped first, then the row is deleted. Credits already spent on a partial run are still billed.

curl -X DELETE https://beta.vilvik.com/api/v1/submissions/abcDEF123456 \
  -H "Authorization: Bearer $VILVIK_KEY"
import vilvik

client = vilvik.Client()

client.submissions.delete("abcDEF123456")

The curl reply is 204 No Content. Scope: submissions:write.

List results for a submission

curl -H "Authorization: Bearer $VILVIK_KEY" \
     "https://beta.vilvik.com/api/v1/results/?submission_id=abcDEF123456"
import vilvik

client = vilvik.Client()

page = client.results.list(submission_id="abcDEF123456")
for r in page:
    print(r.id, r.best_fitness)

Drop submission_id to list every result you own. Scope: results:read.

Fetch a single result

curl -H "Authorization: Bearer $VILVIK_KEY" \
     "https://beta.vilvik.com/api/v1/results/JKLmnop789"
import vilvik

client = vilvik.Client()

result = client.results.get("JKLmnop789")
print(result.best_fitness, result.best_solution)

Returns the best solution genes, the fitness history, and download links. Scope: results:read.

Rename or share a result

A PATCH updates the name or the is_shared flag.

curl -X PATCH https://beta.vilvik.com/api/v1/results/JKLmnop789 \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "renamed result", "is_shared": true}'
import vilvik

client = vilvik.Client()

updated = client.results.update("JKLmnop789", name="renamed result", is_shared=True)
print(updated.is_shared)

Scope: results:write.

Continue from a finished run

Start a new submission from the final population of a finished result.

curl -X POST https://beta.vilvik.com/api/v1/results/JKLmnop789/continue \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"num_generations": 100}'
import vilvik

client = vilvik.Client()

child = client.results.continue_run("JKLmnop789", num_generations=100)
print(child.id, child.status_url)

You may also pass name, webhook_url, or notification_email. Scope: submissions:write. The broader picture is in Continue a submission.

Delete a result

curl -X DELETE https://beta.vilvik.com/api/v1/results/JKLmnop789 \
  -H "Authorization: Bearer $VILVIK_KEY"
import vilvik

client = vilvik.Client()

client.results.delete("JKLmnop789")

The curl reply is 204 No Content. Scope: results:write.

Reuse a fitness function across submissions

Upload a code string once, then reference it by id from many submissions instead of resending the source each time. The _id field name mirrors the parameter (fitness_func becomes fitness_func_id). You still send fitness_func_entry with each submission, since the entry symbol belongs to the run, not the stored snippet.

code_id=$(curl -s -X POST https://beta.vilvik.com/api/v1/code-uploads \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "def fitness_func(ga, sol, idx):\n    return -sum((s-3)**2 for s in sol)\n"}' \
  | jq -r .code_id)

curl -X POST https://beta.vilvik.com/api/v1/submissions/ \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"shared fitness\",\"fitness_func_id\":\"$code_id\",\"fitness_func_entry\":\"fitness_func\",\"num_genes\":4,\"num_generations\":50,\"sol_per_pop\":30,\"num_parents_mating\":4,\"init_range_low\":-2,\"init_range_high\":2}"
import vilvik

client = vilvik.Client()

upload = client.code_uploads.create(
    content=(
        "def fitness_func(ga, sol, idx):\n"
        "    return -sum((s-3)**2 for s in sol)\n"
    ),
)

submission = client.submissions.create(
    name="shared fitness",
    fitness_func_id=upload.code_id,
    fitness_func_entry="fitness_func",
    num_genes=4,
    num_generations=50,
    sol_per_pop=30,
    num_parents_mating=4,
    init_range_low=-2,
    init_range_high=2,
)
print(submission.id)

Scope: submissions:write. See Reusable code uploads.

Get notified instead of polling

Pass a webhook_url on create and we POST to it when the run finishes, so you skip the polling loop entirely.

curl -X POST https://beta.vilvik.com/api/v1/submissions/ \
  -H "Authorization: Bearer $VILVIK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"num_genes":5,"num_generations":50,"sol_per_pop":20,"num_parents_mating":4,"init_range_low":-1,"init_range_high":1,"fitness_func":"def fitness_func(ga, sol, idx):\n    return sum(sol)\n","fitness_func_entry":"fitness_func","webhook_url":"https://example.com/vilvik-webhook"}'
import vilvik

client = vilvik.Client()

submission = client.submissions.create(
    num_genes=5,
    num_generations=50,
    sol_per_pop=20,
    num_parents_mating=4,
    init_range_low=-1,
    init_range_high=1,
    fitness_func=(
        "def fitness_func(ga, sol, idx):\n"
        "    return sum(sol)\n"
    ),
    fitness_func_entry="fitness_func",
    webhook_url="https://example.com/vilvik-webhook",
)
print(submission.id)

See Webhooks for the payload shape, the signature, and how to test a handler locally.

Import an existing submission

Bringing a job over from a notebook or another account is its own endpoint with its own body shape. See Imports endpoint.

Thanks for the feedback!