Product
How credits and success billing work
Every LookValid lookup spends from one credit balance, and most of them charge only when they find something. This post follows a single request through the engine step by step: what is held, what is charged, what is released and what is stored. The table of outcomes at the center lists every case so you can predict a bill before you run a job.
Prices and the two billing rules
Each product has a fixed price in credits per unit, read from the live catalog. For example, an IP lookup is 1 credit and a domain age lookup is 2. The credits and billing page lists every product with its price and unit.
Most products use success billing: you are charged only when the lookup finds something. A few use per-call billing, where every completed lookup is charged whether it found something or not. Today the per-call product is Link Safety Check.
What happens to one request
The engine handles a request in a fixed order, and the order explains most of the table below.
- It checks the input against the product schema. A bad input is refused before anything else happens.
- It checks your account cache. A fresh stored answer for the same product and the same normalized input is returned right away.
- It checks that your account has fewer than 16 lookups running at once. Past that, the request is refused.
- It reserves the product price from your available balance. This is a hold, not a charge. If the balance cannot cover it, the request stops here and the data source is never called.
- It runs the lookup under the product time limit.
- It settles. A found result turns the hold into a charge. A result that was not found releases the hold under success billing and charges it under per-call billing. Errors always release it.
The hold is set to outlast the time limit by a safety margin. If a hold ever expired before settlement, the answer would still be returned and would not be charged. Credits held by lookups still in progress show as reserved in your balance and come back if those lookups find nothing.
The table of outcomes
Every row below follows from the engine order above. Charged means credits taken from your balance, and Stored means the answer is kept in your cache for the window in the next section.
| Outcome | HTTP status | Hold on balance | Charged | Stored |
|---|---|---|---|---|
| Input fails the product schema | 400 | None | 0 | No |
| Same input answered earlier inside the cache window, found or not found | 200 | None | 0 | Already stored, cache_hit is true |
| Account already has 16 lookups running | 429 | None | 0 | No |
| Balance below the product price | 402 | Refused | 0 | No, the data source is not called |
| Found, any product | 200 | Held, then committed | Full product price | Yes |
| Not found, success billing | 200 | Held, then released | 0 | Yes, with the reason |
| Not found, per-call billing | 200 | Held, then committed | Full product price | Yes, with the reason |
| Data source unavailable or lookup timed out | 503 | Held, then released | 0 | No |
| Provider rejects the input after it ran | 400 | Held, then released | 0 | No |
| Unexpected internal error | 500 | Held, then released | 0 | No |
What each outcome costs
On the success-billing rows, found: false is a normal answer with HTTP 200, not an error. It carries credits_charged: 0 and a reason. Branch on found, not on the status code.
The cache window
Results are cached per account, and both found and not found answers are stored. A repeat of the same lookup inside the window returns the stored result with cache_hit: true and charges nothing. Another account that runs the same lookup pays for its own first one. Inputs are normalized before the cache key is built, so letter case or surrounding spaces do not make a new lookup. The longest window is 7 days.
| Window | Products |
|---|---|
| 7 days | phone, country-code, email-domain-check, ip, phone-spam, carrier-lookup, vin-decoder, link-in-bio-lookup, domain-authority, domain-age, website-scraper, website-contacts-scraper |
| 6 hours | link-safety |
| 30 minutes | site-files |
Cache window by product
Two identical requests that are in flight together are handled safely: the first to settle is charged and stored, and the second is returned from that stored entry without a second charge. The cache object in each response shows cached_at and expires_at, so you can see when a stored answer ages out.
Reading the charge and checking your balance
Each lookup response carries credits_charged and credits_remaining, also sent as the X-Credits-Charged and X-Credits-Remaining headers. A call to GET /v1/account returns your granted, spent, reserved and available credits and never costs anything.
Here is a lookup whose cost depends on the result. A registered domain finds data and is charged. An unregistered domain returns found: false and is free, because domain age uses success billing.
curl -X POST https://YOUR_API_HOST/v1/domain-age \
-H "Authorization: Bearer $LOOKVALID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"example.com"}'
Look up a domain, charged only when it is registered
- Do not retry a
found: falseresult. It returns the same answer, free, until the window ends. - Cache results on your side if you need them for longer than the window.
- Check the unit column on the credits and billing page: it says what one charge covers, such as one domain or one VIN.
To see these rules on your own data, run the example above with a key and then read the response headers. The quickstart covers creating the key, and the domain age product page lists the fields in the result.