Workflows

    Provider check before you dispatch a batch

    Run provider-check for every model in a batch before the first POST, so an unroutable name fails the preflight instead of item 87.

    Versely Team8 min read

    The expensive way to learn a model is unroutable is to discover it at item 87 of a 200-item run: 86 completed jobs, a 400 Model not supported, and a manifest you now have to split by hand. GET /api/v1/generate/provider-check answers that question before the first generate POST. It is a routing lookup, not a ping. Use it as a preflight on the distinct names in the batch, and refuse to start if any name comes back available: false.

    It will not tell you that a vendor is having a bad hour. Transient failures still happen after a clean preflight, and those belong in your poll loop. What it does catch is the class of error that never recovers: wrong type, retired name, typo, a slug you copied off a page instead of a catalog name.

    What the endpoint returns

    curl -s "https://api.versely.studio/api/v1/generate/provider-check?model=Flux%20Pro%20Ultra&type=image" \
      -H "Authorization: Bearer $VERSELY_API_KEY"
    

    Required query params: model and type. Missing either is a 400. type must be one of image, video, lipsync, audio, story, background_removal, image_upscale, video_upscale. Anything else is a 400 with that list.

    A 200 looks like:

    {
      "success": true,
      "data": {
        "model": "Flux Pro Ultra",
        "type": "image",
        "provider": "runpod",
        "available": true
      }
    }
    

    available is provider !== null. The provider string is the priority head: the first vendor in the server's chain that hosts that canonical name for that type. You cannot pick a different vendor. A provider field on a later generate POST is discarded. If the head fails at dispatch time, the server walks the rest of the chain itself. The check does not return the chain, only the head (or null).

    available: false with provider: null means: this name, for this type, is on nobody's list. Do not POST it. Do not "try anyway." Generate will 400 with Model "<name>" is not available in any provider, and in a batch that 400 is how you halt a run that was otherwise fine.

    Auth is required, same as generate. This is a cheap GET. It is not a generate, it does not charge credits, and it does not start a job. It still sits on the generate router, so a zero-balance key gets 403 Insufficient credits instead of a routing answer. That is not a missing-scope error; the scope miss reads API key lacks required scope.

    How to put it in front of a dispatcher

    The preflight is a set operation. Two hundred rows that all use VEO 3.1 are one check, not two hundred.

    1. Collect distinct (model, type) pairs from the manifest. If the batch is all image, type is image for every row. If you mixed image and video in one spreadsheet, split the run; the generate endpoints are already split.
    2. Canonicalise nothing yourself. Send the name as you will send it on POST. The check canonicalises aliases the same way generate does. If your spreadsheet says nano banana 2 and generate would accept that, the check will too. If your spreadsheet says a marketing slug, both will fail, which is the point.
    3. Call provider-check once per pair. Persist available and provider on the manifest. Fail the whole preflight if any pair is available: false. Print the bad names and stop. A run that starts with a known-bad name is not a run.
    4. Then cost the batch, then dispatch. Provider-check before credit check, because an unroutable name should not be part of the sum. Estimating credit cost before you dispatch and checking the balance in the agent are the next two gates. There is no free allowance; a 402 mid-batch is the other expensive way to learn you should have preflighted.
    5. Dispatch only the rows that passed. If you must skip a name rather than abort, drop it in the manifest with a reason, do not POST it.

    The batch-from-a-shell-script pattern already splits dispatch from collection and aborts on an empty balance. Provider-check is the gate that belongs above that script's loop, next to "is this duration actually legal for this model."

    Cross-check against GET /api/v1/generate/models if you are building a picker as well as a dispatcher. Provider-check is the per-name question; /generate/models is the full table. A name that is available: true here will appear in that table for the same type. The models catalog is for humans; do not scrape pages as a substitute for this GET.

    What it is not

    Not a health check. available: true means the routing table has a host, not that the host answered a ping. A clean preflight and a later failed with transient: true is a normal pairing. Retry those from the status payload. Do not treat provider-check as "the vendor is up."

    Not a concurrency limiter. Versely enforces rate limits (per-key RPM, plus a tighter limiter on generate). It does not document a "N concurrent generations" cap. Provider-check will not tell you to slow down. 429 with X-RateLimit-Reset will.

    Not a substitute for requires_image. A lipsync name can be available: true and still 400 because you omitted audio_url. An edit name can be available and still 400 because you omitted images. The check answers "will anyone route this name for this type." Input requirements are a different 400, with invalidModels / requiresImages / requiresAudio.

    Not a way to pin a vendor. provider in the response is informational. Logging it on the manifest is useful when you later ask "which head did we expect." Sending it back on generate does nothing.

    Not a live outage switch. If you need to halt a 200-item run because jobs are failing transient in a cluster, that signal is your own: N consecutive retry_suggested failures, then pause. Provider-check will keep returning available: true through that, because the name is still on the list.

    A preflight that actually saves the run

    On a 200-row video batch with three distinct model names, the preflight is three GETs. That is the whole cost.

    What it has caught in practice, every time someone skipped it:

    • A text-to-video column filled with VEO 3.1 Fast I2V. Provider-check with type=video returns available: true because the name is routed. Generate then 400s because that canonical is I2V-only and wants a reference image. Check requires_image (or /ai-models/requires-images) in the same preflight, not after.
    • A marketing slug copied off a landing page that is not a catalog name. Both the check and generate fail, which is the point. Provider-check is cheaper than 200 identical 400s.
    • A type typo: type=image on a video-only name. available: false. Good.

    After a clean preflight, dispatch. Poll. On failed + transient, retry that row. On failed + not transient, record the error and keep going. The developer error ladder is the same one the generate skill uses: 401 dead key, 402 on a generate charge, 403 missing scope (or empty balance on this GET), 429 back off, 400 your payload. Provider-check exists so "model not supported" leaves that ladder before you are 86 items in.

    Plans and the credit balance that a 200-item run will drain sit on pricing. Do not start the run to find out.

    FAQ

    Does a true available mean the generate POST will succeed?

    No. It means the name is routed for that type. Missing images, missing audio, a duration the model does not accept, or a moderation block will still 400. An empty balance fails the credit middleware (403) or the charge (402). Run those other preflights too. Provider-check only removes "this name does not exist here."

    Should I check every row, or every distinct name?

    Distinct (model, type). Checking every row is how you turn a cheap GET into a rate-limit problem of your own making. Cache the result for the length of the run; the routing table does not move mid-batch.

    Why does the response name a provider if I cannot choose one?

    So you can log the head you were going to hit, and so you can see that two names in the same batch may head at different vendors. Fallback still happens behind that head. You do not send the value back.

    Can I use this as a liveness probe in CI?

    You can call it in CI to assert that the names your fixture POSTs are still routed. That is a good test. Do not assert on the provider string remaining constant; heads move. Assert available: true for every name you ship, and available: false for a name you retired on purpose.