Read a QR code
Reading a QR code by eye is impossible and reading one reliably from a phone photo taken at an angle, under warehouse lighting, isn't much easier for a naive decoder. This qr code scanner api takes an image and returns whatever data is encoded inside the code, whether that's a plain URL, a Wi-Fi credential block, a vCard or raw text, so your application never has to embed decoding logic itself.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
Why decoding isn't as trivial as it looks
A QR code's black-and-white grid carries error correction, positioning markers and format information layered on top of the actual payload, all of which a decoder has to locate correctly before it can even begin reading data. A code photographed straight-on under office lighting is easy; the same code photographed at a steep angle, partially shadowed, printed small on a shipping label, or slightly out of focus is where most simple decoding attempts fail. Reliable decoding has to tolerate the messy reality of how people actually photograph things, not just the clean test image.
What POST /dev/qr-decode expects and returns
You send an image containing one or more QR codes. The task locates each code in the frame, decodes it, and returns the raw payload string for each one found, tagged by its position if more than one code appears in the same image. If the image contains no readable code, the task reports that clearly rather than returning an empty or ambiguous result.
The format underneath the scan
QR, short for Quick Response, was designed in 1994 specifically to be read quickly by a scanner moving past it on a factory line, which is why its structure prioritizes fast, error-tolerant reading over information density. That same error correction, built using Reed-Solomon codes, is what lets a code remain readable even when a corner is torn, smudged, or partially covered by a logo, and it's also what lets an image decoder recover a clean payload from an imperfect photo.
What kind of payload comes back
A decoded QR code can hold far more than a URL: plain text, a Wi-Fi network's credentials in a structured format, a vCard contact block, calendar event data, or an arbitrary string an internal system encoded for its own purposes. The endpoint returns exactly what was encoded without assuming a particular use case, leaving interpretation of the payload to whatever system consumes the result.
Fitting into an automated intake pipeline
Warehouse receiving systems that photograph incoming pallets, mobile apps that let a user snap a code instead of typing a link, and document intake pipelines that need to pull embedded QR data from scanned forms all call this endpoint the same way, submitting an image and receiving the payload through a signed webhook or a signed link. Batches of scanned images from a single receiving run process through the same asynchronous model without any special handling.
What you can do with it
Warehouse receiving from photographed pallets
Decode QR labels on incoming shipments straight from a phone photo taken on the loading dock, without a dedicated handheld scanner.
Mobile app onboarding via scanned code
Let a user snap a photo of a QR code to import Wi-Fi credentials or a contact card instead of typing them by hand.
Document intake with embedded QR references
Extract an internal tracking payload encoded into a QR printed on a scanned form during a bulk document processing run.
Verifying printed codes before a shipment goes out
Decode a batch of freshly printed QR labels to confirm each one encodes the correct payload before packaging ships.
FAQ
What formats does this qr code scanner api accept for the image?
Common raster image formats containing a photographed or rendered QR code are accepted; send the clearest available capture for best results.
Can it read a QR code that's blurry or at an angle?
Yes, within reason, the decoder is built to tolerate the imperfect angles and lighting typical of phone photos rather than requiring a flat, straight-on scan.
What happens if the image has no QR code in it?
The task reports clearly that no code was found rather than returning an empty or misleading result.
Can I decode multiple QR codes in a single image?
Yes, each code found in the frame is decoded and returned separately, tagged by its position in the image.
Does this API tell me what type of data was encoded?
It returns the raw decoded payload; whether that string represents a URL, Wi-Fi credentials or plain text is left for your application to interpret.
Is there a free version of this scanner?
The tool above runs free in your browser. The API is paid — each call draws from your prepaid ForHosting KIT balance: top up from $10.00 (it never expires), pay each request's published price, and a call with no balance returns HTTP 402. No subscription, no tokens, and a failed task is never charged.
How much does decoding a QR code cost?
$0.002 per request plus $0.005 per image, with no charge for a task that ultimately fails after retries.
Can I submit a batch of scanned images at once?
Yes, batches run asynchronously, so a full receiving run's worth of images can be submitted together and processed without blocking your app.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/qr-decode \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"image":"https://ejemplo.com/imagen.jpg"}'const res = await fetch("https://api.kit.forhosting.com/dev/qr-decode", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"image": "https://ejemplo.com/imagen.jpg"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/qr-decode",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"image": "https://ejemplo.com/imagen.jpg"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/qr-decode", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"image":"https://ejemplo.com/imagen.jpg"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"image":"https://ejemplo.com/imagen.jpg"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/qr-decode", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"image": "https://ejemplo.com/imagen.jpg"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.qr_decode",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |