QannasAPI

Estimate before you call: price any action for free

What you'll build: a two-step pattern that quotes the cost of a call, checks it against a budget, and only then runs it. You will end with a helper you can wrap around any action in the catalog.

SectionAPI fundamentals
Actions usedPOST /v1/data/{action}:estimatePOST /v1/data/contacts/find-email
CreditsFree — these endpoints never bill
Test-key safeYes — runs on qk_test_ for zero credits
Time to complete~5 min
PrerequisitesA workspace, a qk_test_ or qk_live_ key, and curl or any HTTP client.

The estimate endpoint

Every priced action has a matching estimate. Take the action path, append :estimate, and send the same body you were going to send anyway. You get back the credit cost of that exact call and you are charged nothing for asking.

estimate.sh
# Append :estimate to any action path. Costs nothing.
curl -X POST https://api.qannasapi.com/v1/data/contacts/find-email:estimate \
  -H "Authorization: Bearer $QANNAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "Jane Doe, Acme Logistics"}'

Read the quote

The response uses the standard envelope — status, request_id, data — with the quote inside data. Because estimating is free, the X-Credits-Charged header on an estimate is always 0.

estimate response
# < HTTP/1.1 200 OK
# < X-Credits-Charged: 0     # asking is always free

{
  "status": "OK",
  "request_id": "req_...",
  "data": { /* the quote for this exact call */ }
}

Quote, check, then run

The useful shape is a gate: estimate, compare against whatever budget you hold, and run only if it clears. Both calls take the same body, so the gate costs you one extra request and no credits.

quote_then_call.py
import os, requests

API = "https://api.qannasapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['QANNAS_API_KEY']}"}

def quote_then_call(action, body, budget):
    # 1 — free quote, same body as the real call
    quote = requests.post(
        f"{API}/v1/data/{action}:estimate", json=body, headers=HEADERS
    ).json()

    # 2 — gate on your own budget before spending anything
    if cost_of(quote) > budget:
        raise RuntimeError("quote exceeds budget")

    # 3 — run it
    return requests.post(
        f"{API}/v1/data/{action}", json=body, headers=HEADERS
    )

Workflows quote the same way

Server-side workflows use dry_run instead of a :estimate suffix — send dry_run: true and you get the per-step breakdown without running anything. Same idea, same zero cost.

workflow-quote.sh
# Workflows use dry_run instead of a :estimate suffix.
curl -X POST https://api.qannasapi.com/v1/data/workflows/lead-list \
  -H "Authorization: Bearer $QANNAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"inputs": {"industry": "logistics"}, "limit": 25, "dry_run": true}'

# < X-Credits-Charged: 0   # a dry run never bills

Summary

PatternHow
Quote an actionPOST /v1/data/{action}:estimate with the body you intend to send
Quote a workflowPOST /v1/data/workflows/{name} with dry_run: true
Cost of estimatingZero — X-Credits-Charged is always 0 on an estimate
Envelope{ status, request_id, data } — the same on estimates and real calls
What the number meansWorst case, assuming every step finds something

Production checklist

  • Gate expensive or user-triggered calls behind an estimate.
  • Budget against the estimate, then reconcile against X-Credits-Charged.
  • Quotes keep working at a zero balance — estimating is always free.
  • Send the estimate the same body as the real call, or the quote will not match.

Next steps

Run the estimate first. It costs nothing and it is the same arithmetic the meter will apply.