---
Source: https://docs.microblink.com/blinkid/check-expired
Title: Check if a document is expired
Description: Use the date of expiry from BlinkID scanning results to determine whether a scanned document has expired
---

# Check if a document is expired

BlinkID does not report expiry status directly.
Instead, it returns the expiry date that it read from the document, and you decide whether the document is expired by comparing that date against today.

Two result fields drive this logic:

- `dateOfExpiry`: a date result with optional numeric `year`, `month`, and `day` parts, plus the printed `originalString` and a `successfullyParsed` flag.
- `dateOfExpiryPermanent`: a boolean that is `true` when the document never expires.

## Determining expiry

Apply the following logic to the scanning result:

1. If `dateOfExpiryPermanent` is `true`, the document never expires, so treat it as not expired.
2. If `dateOfExpiry` was not parsed into numeric parts (no `year`), you cannot determine the expiry status.
3. Otherwise, build a date from `year`, `month`, and `day` and compare it against today.

<Tabs queryString="platform">

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

  ```typescript
  type ExpiryStatus = "expired" | "valid" | "unknown";

  function checkExpiry(result): ExpiryStatus {
    if (result.dateOfExpiryPermanent) {
      return "valid";
    }

    const expiry = result.dateOfExpiry;
    if (!expiry?.successfullyParsed || expiry.year === undefined) {
      return "unknown";
    }

    // Month is 1-12, so subtract one for the JS Date constructor.
    const expiryDate = new Date(expiry.year, (expiry.month ?? 1) - 1, expiry.day ?? 1);
    return expiryDate < new Date() ? "expired" : "valid";
  }

  blinkId.addOnResultCallback((result) => {
    console.log("Expiry status:", checkExpiry(result));

    void blinkId.destroy();
  });
  ```

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

  ```kotlin
  import java.time.LocalDate

  enum class ExpiryStatus { EXPIRED, VALID, UNKNOWN }

  fun checkExpiry(result): ExpiryStatus {
      if (result.dateOfExpiryPermanent == true) {
          return ExpiryStatus.VALID
      }

      val expiry = result.dateOfExpiry
      if (expiry?.successfullyParsed != true || expiry.year == null) {
          return ExpiryStatus.UNKNOWN
      }

      // Month is 1-12, which matches LocalDate.
      val expiryDate = LocalDate.of(expiry.year, expiry.month ?: 1, expiry.day ?: 1)
      return if (expiryDate.isBefore(LocalDate.now())) ExpiryStatus.EXPIRED else ExpiryStatus.VALID
  }

  onScanningSuccess = { result ->
      println("Expiry status: ${checkExpiry(result)}")
  }
  ```

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

  ```swift
  enum ExpiryStatus {
      case expired, valid, unknown
  }

  func checkExpiry(_ result) -> ExpiryStatus {
      if result.dateOfExpiryPermanent == true {
          return .valid
      }

      guard let expiry = result.dateOfExpiry,
            expiry.successfullyParsed,
            let year = expiry.year else {
          return .unknown
      }

      // Month is 1-12, which matches DateComponents.
      var components = DateComponents()
      components.year = year
      components.month = expiry.month ?? 1
      components.day = expiry.day ?? 1

      guard let expiryDate = Calendar.current.date(from: components) else {
          return .unknown
      }
      return expiryDate < Date() ? .expired : .valid
  }

  onScanCompleted: { resultState in
      guard let result = resultState.scanningResult else { return }
      print("Expiry status:", checkExpiry(result))
  }
  ```

  </TabItem>
</Tabs>

## Handling the unknown case

When the expiry date cannot be parsed, fall back to the printed text in `originalString` rather than rejecting the document outright.
This lets you surface the raw expiry text to a reviewer or to the user for manual confirmation.

<Tabs queryString="platform">

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

  ```typescript
  blinkId.addOnResultCallback((result) => {
    if (!result.dateOfExpiryPermanent && !result.dateOfExpiry?.successfullyParsed) {
      console.log("Could not parse expiry. Printed text:", result.dateOfExpiry?.originalString?.latin?.value);
    }

    void blinkId.destroy();
  });
  ```

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

  ```kotlin
  onScanningSuccess = { result ->
      if (result.dateOfExpiryPermanent != true && result.dateOfExpiry?.successfullyParsed != true) {
          println("Could not parse expiry. Printed text: ${result.dateOfExpiry?.originalString?.value}")
      }
  }
  ```

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

  ```swift
  onScanCompleted: { resultState in
      guard let result = resultState.scanningResult else { return }
      if result.dateOfExpiryPermanent != true && result.dateOfExpiry?.successfullyParsed != true {
          print("Could not parse expiry. Printed text:", result.dateOfExpiry?.originalString?.value ?? "")
      }
  }
  ```

  </TabItem>
</Tabs>

## Related articles

- [Result reference](./result-reference.md)
- [Extract specific fields](./extract-fields.md)
- [Scanning settings](./scanning-settings.md)


Last updated on Jul 24, 2026
