---
Source: https://docs.microblink.com/blinkid/v8000/scan-image
Title: Scan from a static image
Description: Scan identity documents from image files or gallery uploads without a live camera feed
---

# Scan from a static image

For image file or gallery upload flows, use the SDK without the camera UI.
This lets you feed images into the scanning engine one at a time.

## Initialize and create a session

Initialize the SDK and create a session configured for photo input.
Setting the input image source to photo tells the engine to expect static images rather than a video stream.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  ```typescript
  import { loadBlinkIdCore } from "@microblink/blinkid-core";

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

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

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

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

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

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

  ```swift
  let sdk = try await BlinkIDSdk.createBlinkIDSdk(
      withSettings: BlinkIDSdkSettings(licenseKey: "your-license-key")
  )

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

  </TabItem>
  <TabItem value="flutter" label="Flutter (Dart)">

  Flutter scans static images through DirectAPI.
  Configure the SDK and session settings up front, then pass them to `performDirectApiScan` along with the image data.

  ```dart
  import 'package:blinkid_flutter/blinkid_flutter.dart';
  import 'package:flutter/services.dart';

  final blinkIdPlugin = BlinkIdFlutter();

  final sdkSettings = BlinkIdSdkSettings(licenseKey: "your-license-key");
  sdkSettings.downloadResources = true;

  final sessionSettings = BlinkIdSessionSettings(
    scanningMode: ScanningMode.automatic,
    scanningSettings: BlinkIdScanningSettings(
      documentCaptureModule: DocumentCaptureModuleSettings(documentImageReturnEnabled: true),
      mrzModule: MrzModuleSettings(),
      barcodeModule: BarcodeModuleSettings(),
      vizModule: VizModuleSettings(),
    ),
  );
  ```

  </TabItem>
  <TabItem value="react-native" label="React Native (TS)">

  React Native scans static images through DirectAPI.
  Prepare the SDK and session settings up front, then pass them to `performDirectApiScan` along with the image data.

  ```typescript
  import { performDirectApiScan } from "@microblink/blinkid-react-native";

  const sdkSettings = {
    licenseKey: "your-license-key",
    downloadResources: true,
  };

  const sessionSettings = {
    scanningMode: "automatic",
    scanningSettings: {
      documentCaptureModule: { documentImageReturnEnabled: true, inputImageCropped: false },
      mrzModule: {},
      barcodeModule: { pdf417ScanningEnabled: true, qrScanningEnabled: true },
      vizModule: {},
    },
  };
  ```

  </TabItem>
</Tabs>

## Load an image for scanning

Each platform requires converting a raw image into the format the scanning engine expects.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  The scanning engine accepts `ImageData` objects.
  Use an offscreen canvas to decode a `File` or `Blob` into the required format.

  ```typescript
  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)">

  The scanning engine requires an `ARGB_8888` bitmap wrapped in an `InputImage`.

  ```kotlin
  val bitmap = BitmapFactory.decodeFile(imagePath)
      ?.copy(Bitmap.Config.ARGB_8888, false)
      ?: return

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

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

  Wrap a `UIImage` in an `InputImage` to pass it to the scanning engine.

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

  </TabItem>
  <TabItem value="flutter" label="Flutter (Dart)">

  DirectAPI accepts Base64-encoded image strings.
  Read the image files from disk or gallery and encode them.

  ```dart
  import 'dart:convert';
  import 'dart:io';

  final frontImageBase64 = base64Encode(await File(frontImagePath).readAsBytes());
  final backImageBase64 = base64Encode(await File(backImagePath).readAsBytes());
  ```

  </TabItem>
  <TabItem value="react-native" label="React Native (TS)">

  DirectAPI accepts Base64-encoded image strings.
  Read the image files from disk or gallery and encode them, for example with `react-native-fs`.

  ```typescript
  import RNFS from "react-native-fs";

  const firstImageBase64 = await RNFS.readFile(frontImagePath, "base64");
  const secondImageBase64 = await RNFS.readFile(backImagePath, "base64");
  ```

  </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 document's other side before it can return a final result.
When the status is `success`, retrieve the final result.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  ```typescript
  // Process the front side
  const frontImageData = await fileToImageData(frontFile);
  const frontProcessResult = await session.process(frontImageData);
  const status = frontProcessResult.inputImageAnalysisResult.processingStatus;

  if (status === "awaiting-other-side") {
    // The document has a back side — process it too
    const backImageData = await fileToImageData(backFile);
    await session.process(backImageData);
  }

  // Retrieve the final result
  const result = await session.getResult();
  console.log("First name:", result.firstName?.latin?.value);
  console.log("Document number:", result.documentNumber?.latin?.value);
  console.log("Date of expiry:", result.dateOfExpiry?.originalString?.latin?.value);
  ```

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

  ```kotlin
  // Process the front side
  val frontBitmap = BitmapFactory.decodeFile(frontImagePath)
      ?.copy(Bitmap.Config.ARGB_8888, false) ?: return
  val frontProcessResult = session.process(
      InputImage.createFromBitmap(frontBitmap)
  ).getOrThrow()

  if (frontProcessResult.inputImageAnalysisResult.processingStatus
          == ProcessingStatus.AwaitingOtherSide) {
      // The document has a back side — process it too
      val backBitmap = BitmapFactory.decodeFile(backImagePath)
          ?.copy(Bitmap.Config.ARGB_8888, false) ?: return
      session.process(InputImage.createFromBitmap(backBitmap)).getOrThrow()
  }

  // Retrieve the final result
  val result = session.getResult().getOrNull()
  println("First name: ${result?.firstName?.value}")
  println("Document number: ${result?.documentNumber?.value}")
  ```

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

  ```swift
  // Process the front side
  let frontFrameResult = try await session.process(inputImage: InputImage(uiImage: frontImage))

  if frontFrameResult.processResult?.inputImageAnalysisResult.processingStatus
          == .awaitingOtherSide {
      // The document has a back side — process it too
      try await session.process(inputImage: InputImage(uiImage: backImage))
  }

  // Retrieve the final result
  let result = session.getResult()
  print("First name:", result?.firstName?.value ?? "")
  print("Document number:", result?.documentNumber?.value ?? "")
  ```

  </TabItem>
  <TabItem value="flutter" label="Flutter (Dart)">

  Pass both images to `performDirectApiScan`.
  Provide `secondImage` only when the document has a back side; for a single-sided document, omit it and use `ScanningMode.single`.

  ```dart
  try {
    final result = await blinkIdPlugin.performDirectApiScan(
      blinkIdSdkSettings: sdkSettings,
      blinkIdSessionSettings: sessionSettings,
      firstImage: frontImageBase64,
      secondImage: backImageBase64, // optional; omit for single-side
    );

    if (result != null) {
      print("First name: ${result.firstName?.value}");
      print("Document number: ${result.documentNumber?.value}");
    }
  } on PlatformException catch (e) {
    print(e.message);
  }
  ```

  </TabItem>
  <TabItem value="react-native" label="React Native (TS)">

  Pass both images to `performDirectApiScan`.
  Provide `secondImage` only when the document has a back side; for a single-sided document, omit it and set `scanningMode` to `"single"`.

  ```typescript
  try {
    const result = await performDirectApiScan({
      sdkSettings,
      sessionSettings,
      firstImage: firstImageBase64,
      secondImage: secondImageBase64, // optional; omit for single-side
    });

    console.log("First name:", result.firstName?.value);
    console.log("Full name:", result.fullName?.value);
  } catch (error) {
    console.error(error);
  }
  ```

  </TabItem>
</Tabs>

## Clean up

Release the session when done to free scanning resources.
On Android and iOS, closing or releasing the session is sufficient.
On web, also terminate the core instance.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  ```typescript
  await session.delete();
  await blinkIdCore.terminate();
  ```

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

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

  </TabItem>
  

<!-- interactive component omitted -->


  

<!-- interactive component omitted -->


  

<!-- interactive component omitted -->


</Tabs>

## Related articles

- [How to scan in real time via camera](scan-camera.md)
- [How to scan a single-sided document](scan-single-side.md)
- [How to scan a double-sided document](scan-double-side.md)
- [How to extract specific fields](extract-fields.md)


Last updated on Jul 24, 2026
