---
Source: https://docs.microblink.com/blinkcard/scan-image
Title: Scan a card from a static image
Description: Scan a payment card from an image file or upload without a live camera using BlinkCard
---

# Scan a card from a static image

For file upload or gallery flows, use the lower-level scanning session directly, without the camera UI.
This lets you feed images into the scanning engine one at a time.

## Initialize and create a session

Create a session with the input image source set to `photo`.
This tells the engine to expect static images rather than a video stream.

On Web, load the core engine with `loadBlinkCardCore`, then create the session.
On Android, initialize the SDK, then create the session.
On iOS, create the SDK, then create the session.

<Tabs queryString="platform">
  <TabItem value="web" label="Web (JS)" default>

```ts

const blinkCardCore = await loadBlinkCardCore({
  licenseKey: "your-license-key",
});

const session = await blinkCardCore.createScanningSession({
  inputImageSource: "photo",
});
```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

```kotlin
val sdk = BlinkCardSdk.initializeSdk(
    context,
    BlinkCardSdkSettings(licenseKey = "your-license-key")
).getOrThrow()

val session = sdk.createScanningSession(
    BlinkCardSessionSettings(inputImageSource = InputImageSource.Photo)
)
```

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

```swift
let sdk = try await BlinkCardSdk.createBlinkCardSdk(
    withSettings: BlinkCardSdkSettings(licenseKey: "your-license-key")
)

let session = try await sdk.createScanningSession(
    sessionSettings: BlinkCardSessionSettings(inputImageSource: .photo)
)
```

  </TabItem>
</Tabs>

For installation, license setup, and hosting the WASM resources, see the [quickstart](./quickstart.md).

## Load an image for scanning

Each platform wraps a decoded image in the format the scanning engine expects.

On Web, the engine accepts `ImageData`.
Use an offscreen canvas to decode a `File` or `Blob` into the required format.

On Android, build an `InputImage` from an ARGB_8888 `Bitmap` loaded from the gallery or a file.

On iOS, build an `InputImage` from a `UIImage` loaded from the photo library or a file.

<Tabs queryString="platform">
  <TabItem value="web" label="Web (JS)" default>

```ts
async function fileToImageData(file: File): Promise<ImageData> {
  const img = new Image();
  const url = URL.createObjectURL(file);
  img.src = url;

  try {
    await new Promise<void>((resolve) => {
      img.onload = () => resolve();
    });
    const canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    const ctx = canvas.getContext("2d");
    if (!ctx) throw new Error("Could not get canvas context");
    ctx.drawImage(img, 0, 0);
    return ctx.getImageData(0, 0, canvas.width, canvas.height);
  } finally {
    URL.revokeObjectURL(url);
  }
}
```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

```kotlin
val inputImage = InputImage.createFromBitmap(bitmap)
```

The bitmap must be in `ARGB_8888` configuration.

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

```swift
let inputImage = InputImage(uiImage: image)
```

  </TabItem>
</Tabs>

## Process images and get the result

Pass each image to the session and check the processing status after each call.
A status of `awaiting-other-side` means the engine needs the card's other side before it can return a final result.
When the card is fully scanned, retrieve the result with `getResult`.

<Tabs queryString="platform">
  <TabItem value="web" label="Web (JS)" default>

```ts
// Process the first side (the side with the card number)
const firstImageData = await fileToImageData(firstFile);
const firstProcessResult = session.process(firstImageData);
const status = firstProcessResult.inputImageAnalysisResult.processingStatus;

if (status === "awaiting-other-side") {
  // The card has more data on the other side — process it too
  const secondImageData = await fileToImageData(secondFile);
  session.process(secondImageData);
}

// Retrieve the final result
const result = session.getResult();
console.log("Card number:", result.cardAccounts[0].cardNumber);
console.log("Expiry:", result.cardAccounts[0].expiryDate.originalString);
```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

```kotlin
// Process the first side (the side with the card number)
val firstResult = session.process(
    InputImage.createFromBitmap(firstBitmap)
).getOrThrow()

if (firstResult.inputImageAnalysisResult.processingStatus
        == ProcessingStatus.AwaitingOtherSide) {
    // The card has more data on the other side — process it too
    session.process(InputImage.createFromBitmap(secondBitmap)).getOrThrow()
}

// Retrieve the final result
val result = session.getResult()
val account = result.cardAccounts[0]
println("Card number: ${account.cardNumber}")
println("Expiry: ${account.expiryDate?.originalString}")
```

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

```swift
// Process the first side (the side with the card number)
let firstResult = try await session.process(inputImage: InputImage(uiImage: firstImage))

if firstResult.processResult?.inputImageAnalysisResult.processingStatus == .awaitingOtherSide {
    // The card has more data on the other side — process it too
    try await session.process(inputImage: InputImage(uiImage: secondImage))
}

// Retrieve the final result
let result = session.getResult()
let account = result.cardAccounts[0]
print("Card number:", account.cardNumber)
print("Expiry:", account.expiryDate?.originalString ?? "")
```

  </TabItem>
</Tabs>

## Clean up

Release the session and SDK when done.

On Web, call `session.delete()`, then terminate the core instance.

On Android, close the session and the SDK to release native resources.

On iOS, the session releases its resources when it is deallocated; terminate the SDK when you are done with it.

<Tabs queryString="platform">
  <TabItem value="web" label="Web (JS)" default>

```ts
session.delete();
await blinkCardCore.terminate();
```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

```kotlin
session.close()
sdk.close()
```

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

```swift
BlinkCardSdk.terminateBlinkCardSdk()
```

  </TabItem>
</Tabs>

## Related articles

- [Scan a card via camera](./scan-camera.md)
- [Extract specific fields](./extract-fields.md)


Last updated on Jun 18, 2026
