# Schengen 90/180 & Residence Day Calculator API (`fetchgrove/stay-limit-calculator`) Actor

Count days for the Schengen 90/180 rule, UK settlement and Statutory Residence Test, US substantial presence and 330-day tests, and Canadian citizenship and PR. Get days used, days left, the first breach date and whether a planned trip fits. Every rule cites its official source.

- **URL**: https://apify.com/fetchgrove/stay-limit-calculator.md
- **Developed by:** [fetchgrove](https://apify.com/fetchgrove) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 rule checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Schengen 90/180 & Residence Day Calculator API

Give it travel dates and get the answer immigration and tax rules actually ask: how many days count, how many are left, the first day a limit was broken, and whether a planned trip fits. One engine, nine rules, each counting travel days the way its own official guidance says.

No scraping and no outside calls: the calculation runs on the dates you send, and results go only to your own run storage.

### Rules

| Rule | Question it answers | How a travel day counts |
|---|---|---|
| `schengen-90-180` | Short stays: at most 90 days in any 180-day period across the Schengen area | entry and exit days both count (European Commission method) |
| `uk-settlement-180` | UK settlement: absences of at most 180 days in any 12 months | whole days abroad only; travel days are not absence days |
| `uk-naturalisation` | UK citizenship by naturalisation: at most 450 days outside the UK in 5 years (270 in 3 years for a British citizen's spouse or civil partner), at most 90 in the last 12 months, and present on the first day | whole days abroad only; Jersey, Guernsey and the Isle of Man count as the UK |
| `uk-srt-day-count` | UK days per tax year (6 April to 5 April) and the 183-day automatic UK test | a day counts if you are in the UK at the end of it |
| `us-substantial-presence` | US substantial presence test for a calendar year | any part of a day counts; weighted over three years |
| `us-330-day` | US physical presence test for the foreign earned income exclusion | only full days outside the US; finds the best 12-month window |
| `canada-citizenship-1095` | Canadian citizenship: 1,095 days in the 5 years before applying, with half-day credit for time before permanent residence | departure and return days count as days in Canada |
| `canada-pr-730` | Canadian permanent residence: 730 days in the last 5 years | same as citizenship |
| `rolling-183` | Days in one country in any 12-month period and per calendar year | any part of a day counts |

Each result names its official source and the date it was checked.

### What you get

One row per traveller and rule: days counted, the limit, days remaining, status, the window used, the first breach date and every period over the limit, the earliest date you become eligible (citizenship), and for a planned trip: does it fit, how long you can stay, the latest exit and the earliest entry that works. Every row also lists what the rule does not model (for example visa-specific limits) and any warnings.

### Use it for

- **Travel and relocation apps**: check a customer's itinerary before they book.
- **HR and mobility teams**: flag employees close to a Schengen, UK or US day limit.
- **Immigration and tax advisers**: a fast first count before the detailed review.
- **AI agents**: "can I spend 20 more days in France this summer?" answered exactly.

### How to use it

Add travellers, each with an `ref` label of your choice (no names needed) and a list of trips `{ country, from, to }` with ISO country codes and inclusive dates. Choose the rules, and optionally an `asOf` date and a planned trip. For the Canadian rules, list your trips abroad and add your permanent residence date.

Or paste trips as plain lines, one per trip: `FR 2026-03-01 2026-03-30` (dashes, `to` and commas work too; `#` starts a comment).

Leave the input empty and it runs one example traveller.

### Output example

Real output (shortened): 75 days in Spain and 31 in France, checked on 1 June 2026 with a planned 20-day trip to France from 1 July.

```json
{
  "ref": "T1",
  "rule": "schengen-90-180",
  "daysCounted": 106,
  "limit": 90,
  "remaining": -16,
  "status": "over",
  "firstBreachDate": "2026-05-16",
  "plan": { "entry": "2026-07-01", "days": 20, "fits": false, "maxStay": 0, "earliestEntry": "2026-07-20" }
}
```

### Pricing

| What | Price |
|---|---|
| One rule checked for one traveller | $0.004 |
| Planned-trip answer (fits, latest exit, earliest entry) | $0.008, on top |
| Run start | $0.00005 per run (5 cents per 1,000 runs) |

Rows with invalid input are free. Free Apify plan: up to 20 results per run.

### Important

Not legal or tax advice. Rules have exceptions this tool does not model (they are listed in each result). Check the official rules or a qualified adviser before you rely on a result.

### Support

Questions or a rule you would like added: open an issue on this page.

# Actor input Schema

## `travellers` (type: `array`):

One entry per traveller: { "ref": "t1", "trips": \[{ "country": "FR", "from": "2026-05-01", "to": "2026-05-20" }] }. ref is your own opaque label (letters, digits, dots, dashes, underscores or colons): never a name, email, phone or document number. Each trip is a stay in one country, both dates inclusive (YYYY-MM-DD): from is the day you arrived, to the day you left. country is an ISO 3166-1 code (FR, GB, US, CA) or a subdivision where it matters (ES-CN Canary Islands, PT-20 Azores, FR-GF French Guiana). Optional per trip: status (confirmed, planned or estimated). Instead of (or as well as) trips, a traveller can have tripsText: plain text with one trip per line, such as "FR 2026-03-01 2026-03-30", "ES-CN 2026-01-05 to 2026-01-20" or "US 2026-02-10..2026-02-20" (# starts a comment; blank lines are ignored; a tripsText that is blank or only comments adds no trips and leaves the traveller's trips as they are). Optional per traveller, for the Canada rules: prDate (the day you became a permanent resident) and temporaryResidencePeriods (\[{ from, to }] when you were in Canada as a temporary resident or protected person before that). For rules that count absences (UK settlement, Canada), days not inside any listed trip are treated as days at home, so list every trip abroad. Leave empty to run one built-in example traveller.

## `tripsText` (type: `string`):

The quickest way to check one traveller: one trip per line, written COUNTRY ARRIVAL DEPARTURE with dates as YYYY-MM-DD, both days included. Examples: FR 2026-03-01 2026-03-30, ES-CN 2026-01-05 to 2026-01-20, US 2026-02-10..2026-02-20. Fields may also be separated by commas or tabs, and a status (planned or estimated) may follow the dates. Lines starting with # and blank lines are ignored; anything after # is a comment. Empty text is ignored; text with comments only and no trip lines gives an error row for this traveller (uncharged). A line that cannot be read gives an error row for this traveller, naming the line, and nothing is charged. If Travellers is also filled in, this text is one more traveller.

## `rules` (type: `array`):

Rules to check for every traveller. Each traveller and rule gives one result row.

## `asOf` (type: `string`):

The date to report on (YYYY-MM-DD). Trips after it are included as planned travel. Defaults to today (UTC).

## `plan` (type: `object`):

Optional question about a planned trip: { "entry": "2026-11-01", "days": 30, "country": "FR" }. For each rule it applies to, the result says whether it fits, the longest stay from that entry (maxStay, latestExit) and the earliest entry for a stay of that length. For UK settlement and Canada PR the planned trip is an absence abroad.

## `applicationDate` (type: `string`):

For canada-citizenship-1095 and uk-naturalisation: the date you will sign the application, or for the UK the date the Home Office receives it (YYYY-MM-DD). Defaults to the As of date.

## `rolling183Country` (type: `string`):

For rolling-183: the ISO 3166-1 alpha-2 country to count (for example ES). If empty, the planned country is used, or else the country with the most days.

## `ukNaturalisationRoute` (type: `string`):

For uk-naturalisation. standard: section 6(1), 5-year qualifying period, at most 450 days outside the UK. spouse: section 6(2), for the husband, wife or civil partner of a British citizen, 3-year period, at most 270 days. Both: at most 90 days outside the UK in the final 12 months.

## Actor input object example

```json
{
  "travellers": [
    {
      "ref": "t1",
      "trips": [
        {
          "country": "FR",
          "from": "2026-03-01",
          "to": "2026-03-30"
        },
        {
          "country": "GB",
          "from": "2026-04-10",
          "to": "2026-04-20"
        },
        {
          "country": "ES",
          "from": "2026-06-01",
          "to": "2026-07-10"
        },
        {
          "country": "IT",
          "from": "2026-09-20",
          "to": "2026-10-05"
        }
      ]
    }
  ],
  "rules": [
    "schengen-90-180"
  ],
  "plan": {
    "entry": "2026-11-01",
    "days": 30,
    "country": "FR"
  },
  "ukNaturalisationRoute": "standard"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per traveller and rule: days counted, limit, status and any plan answer.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "travellers": [
        {
            "ref": "t1",
            "trips": [
                {
                    "country": "FR",
                    "from": "2026-03-01",
                    "to": "2026-03-30"
                },
                {
                    "country": "GB",
                    "from": "2026-04-10",
                    "to": "2026-04-20"
                },
                {
                    "country": "ES",
                    "from": "2026-06-01",
                    "to": "2026-07-10"
                },
                {
                    "country": "IT",
                    "from": "2026-09-20",
                    "to": "2026-10-05"
                }
            ]
        }
    ],
    "rules": [
        "schengen-90-180"
    ],
    "plan": {
        "entry": "2026-11-01",
        "days": 30,
        "country": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchgrove/stay-limit-calculator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "travellers": [{
            "ref": "t1",
            "trips": [
                {
                    "country": "FR",
                    "from": "2026-03-01",
                    "to": "2026-03-30",
                },
                {
                    "country": "GB",
                    "from": "2026-04-10",
                    "to": "2026-04-20",
                },
                {
                    "country": "ES",
                    "from": "2026-06-01",
                    "to": "2026-07-10",
                },
                {
                    "country": "IT",
                    "from": "2026-09-20",
                    "to": "2026-10-05",
                },
            ],
        }],
    "rules": ["schengen-90-180"],
    "plan": {
        "entry": "2026-11-01",
        "days": 30,
        "country": "FR",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fetchgrove/stay-limit-calculator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "travellers": [
    {
      "ref": "t1",
      "trips": [
        {
          "country": "FR",
          "from": "2026-03-01",
          "to": "2026-03-30"
        },
        {
          "country": "GB",
          "from": "2026-04-10",
          "to": "2026-04-20"
        },
        {
          "country": "ES",
          "from": "2026-06-01",
          "to": "2026-07-10"
        },
        {
          "country": "IT",
          "from": "2026-09-20",
          "to": "2026-10-05"
        }
      ]
    }
  ],
  "rules": [
    "schengen-90-180"
  ],
  "plan": {
    "entry": "2026-11-01",
    "days": 30,
    "country": "FR"
  }
}' |
apify call fetchgrove/stay-limit-calculator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchgrove/stay-limit-calculator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/uR34K8o4QWw5zCAPn/builds/YchfMxPJelyh4z1Q4/openapi.json
