Getting started
Quick start with the FrankLab API: base URL, key, first request.
Apergrex uses FrankLab API to generate and process content from your applications. This page walks through one complete Seedance 2.5 by BytePlus example. Start with Connect to the API, then choose video generation, images, text and audio, or utilities and montage. These guides map Make module operations to REST requests; the reference covers billing, keys and webhooks.
Base URL
https://apergrex.ru/franklab/apiThe same API is served at https://franklab.ru/franklab/api.
Get an API key
- Sign in to the apergrex.ru cabinet and open Settings → API.
- Click "Create key" — you receive a value like
fl_live_…. - The raw key is shown exactly once — store it immediately. Treat it like a password: it grants access to your balance and partner operations.
Your key list is also available programmatically (GET /v1/billing/api-keys, see API reference → api-keys); minting and revoking keys requires the signed-in cabinet session, so a key itself can never issue further keys.
First generation: text to video
Create a Seedance 2.5 by BytePlus · 4-second · 720p · 16:9 video without uploading files. You need Bash, cURL, jq and an API key with access to your balance. Run the steps in order: stop on an error and inspect the response before continuing.
The example uses the public Make MOON routes. The moon_pro alias selects Seedance 2.5; the API keeps the underlying provider model identifier server-side. The estimate call is read-only. Its amount is a current estimate in Franks, not a fixed price or a promise of final cost.
1. Prepare your key
Enter your key in the terminal: input is hidden and is not stored in command history. Never put it in browser JavaScript or a public repository.
read -rsp 'API key: ' FRANKLAB_KEY; echo
export FRANKLAB_KEY
export FRANKLAB_BASE_URL='https://apergrex.ru/franklab/api'
export FRANKLAB_IDEMPOTENCY_KEY="apergrex-seedance-$(date +%s)-$$"2. Check your available balance
curl --fail-with-body --max-time 30 \
-H "X-API-Key: ${FRANKLAB_KEY:?Set your API key}" \
"$FRANKLAB_BASE_URL/v1/billing/balance" \
-o balance.json
jq '{available, reserved}' balance.jsonavailable is the amount available for new operations, in Franks. reserved is already held for existing operations. Top up in the cabinet if necessary, then check again.
3. Estimate the cost
curl --fail-with-body --max-time 30 -X POST \
-H "X-API-Key: ${FRANKLAB_KEY:?Set your API key}" \
-H 'Content-Type: application/json' \
--data-binary @- \
"$FRANKLAB_BASE_URL/make/moon/estimate" \
-o estimate.json <<JSON
{"operation":"text_to_video","moonModel":"moon_pro","prompt":"A silver running shoe on a white pedestal, slow camera orbit in soft studio light, no logos or text.","duration":4,"resolution":"720p","ratio":"16:9","generate_audio":false,"idempotency_key":"${FRANKLAB_IDEMPOTENCY_KEY}"}
JSON
jq '.data | {estimated_cost_franks, cost_currency, cost_unit, cost_status}' estimate.jsonStop if the estimate request fails, .data.estimated_cost_franks is missing or nonnumeric, or available is missing, nonnumeric, or too low. Do not submit without a valid estimate and sufficient balance. The estimate starts no task and reserves no funds. It does not lock the price; the final amount may depend on the current tariff and task usage. cost_unit describes the returned accounting unit; do not infer a per-second rate from this example.
For 401/403, check the key; for 429, wait before an allowed retry. See Errors.
4. Submit once
The next command creates a paid task and may reserve funds. Run it once, after checking the estimate and balance. It uses the same settings and idempotency key as the estimate.
curl --fail-with-body --max-time 120 -X POST \
-H "X-API-Key: ${FRANKLAB_KEY:?Set your API key}" \
-H 'Content-Type: application/json' \
--data-binary @- \
"$FRANKLAB_BASE_URL/make/moon/videos" \
-o submit.json <<JSON
{"operation":"text_to_video","moonModel":"moon_pro","prompt":"A silver running shoe on a white pedestal, slow camera orbit in soft studio light, no logos or text.","duration":4,"resolution":"720p","ratio":"16:9","generate_audio":false,"idempotency_key":"${FRANKLAB_IDEMPOTENCY_KEY}"}
JSON
jq '.data | {taskId, status, final_cost_franks, cost_unit, cost_status}' submit.jsonAfter a successful response, save the returned data.taskId (or data.task_id):
FRANKLAB_TASK_ID=$(jq -er '(.data.taskId // .data.task_id) | select(type == "string" and length > 0)' submit.json)
export FRANKLAB_TASK_IDA timeout, 5xx, or response without a task ID leaves the submission outcome unknown. Do not repeat the POST with a new idempotency key: the task may have been accepted. Reconcile cabinet task history; if no identifier is found, contact support with the time, model and response without your key. If an explicit retry is agreed, reuse the same idempotency key and exact request body.
5. Poll the same task
curl --fail-with-body --max-time 30 \
-H "X-API-Key: ${FRANKLAB_KEY:?Set your API key}" \
"$FRANKLAB_BASE_URL/make/moon/status/${FRANKLAB_TASK_ID:?Set the returned task ID}" \
-o status.json
jq '.data | {taskId, status, final_cost_franks, cost_unit, result, videoUrl, video_url, error_message, error}' status.jsonRepeat only this GET approximately every 5 seconds, up to 60 checks per polling cycle. This is a client limit, not a promised generation time. On 429, increase the delay according to the response; on 401/403/404, stop and investigate. Reaching the polling limit does not cancel the task: retain its ID and resume GET with that ID later.
data.status | Action |
|---|---|
queued, processing | Still running; continue bounded polling. |
completed | Read the video URL from data.videoUrl or data.video_url (also available as data.result.video_url). |
failed | Stop polling, read data.error_message or data.error, and reconcile balance and transactions. |
| Any other value | Retain the response and ID and clarify the state; never automatically create a replacement task. |
6. Get the video and reconcile billing
jq -er '.data | select(.status == "completed") | (.videoUrl // .video_url // .result.video_url) | select(type == "string" and startswith("https://"))' status.jsonOpen the returned HTTPS URL or save the file to your storage. If the task completes without a URL, retain the response and contact support; another generation does not repair result delivery. This guide does not guarantee a URL retention period.
final_cost_franks is the final amount once settlement completes; null does not mean the generation was free. Reconcile it against balance and transactions. Do not derive the final charge from the estimate.
Next steps
- Authentication — keys, headers, limits
- Billing — balance, reservations, transactions, tariffs
- API reference — billing, keys and webhooks
- Pricing — the base price list