# Open-Meteo Scraper: Global Weather & Historical Archive (`arman-bd/open-meteo-weather-scraper`) Actor

Scrape Open-Meteo for global weather: current conditions, hourly and daily forecasts, and historical reanalysis back to 1940. Any coordinate or place name, no key.

- **URL**: https://apify.com/arman-bd/open-meteo-weather-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 weather scrapeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Open-Meteo Scraper: Global Weather & Historical Archive

![Open-Meteo Scraper: Current conditions, hourly and daily forecasts out to 16 days, and hourly-resolution history back to 1940, for any coordinate or place name](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/open-meteo-weather-scraper.jpg)

Returns weather for any coordinate on earth: current conditions, hourly and daily forecasts out to 16 days, and reanalysis history stretching back to 1940. One Actor covers both, with a single switch between the two modes.

Open-Meteo is an open weather API with no key, no signup and no quota for non-commercial use. There's no browser, no proxy and no login. Place names are resolved through Open-Meteo's own geocoder, so `"Tokyo"` is as valid an input as `"35.68,139.69"`.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/open-meteo-weather-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/open-meteo-weather-scraper.md
```

### What you get

| Field | What it holds |
|---|---|
| `location` | The location exactly as you passed it |
| `resolvedName`, `admin1`, `country`, `countryCode` | Geocoder match for place-name inputs (null when you passed coordinates) |
| `latitude`, `longitude`, `elevation` | The grid cell Open-Meteo actually served, plus its elevation in metres |
| `timezone`, `timezoneAbbreviation`, `utcOffsetSeconds` | Timezone the series timestamps are expressed in |
| `mode` | `forecast` or `historical` |
| `currentTemperature`, `currentApparentTemperature`, `currentRelativeHumidity` | Live conditions (forecast mode only) |
| `currentPrecipitation`, `currentWindSpeed`, `currentWindDirection` | Live conditions, continued |
| `currentWeatherCode`, `currentWeatherDescription` | WMO code plus its plain-English meaning |
| `hourly`, `daily` | Column-oriented forecast series: a `time` array plus one array per variable |
| `historicalSeries` | The same structure for archive runs, holding `hourly` and `daily` |
| `units` | Unit string for every variable returned, split by `current`, `hourly` and `daily` |
| `sourceUrl` | The exact Open-Meteo URL used, so any record is reproducible by hand |
| `scrapedAt` | Run timestamp |

`RUN_SUMMARY` in the key-value store holds per-run counts, the settings you used, and any location that failed.

### Use cases

- **Backtesting against sales.** Pull ten years of daily highs for every store postcode.
- **Agricultural monitoring.** Soil moisture and precipitation series for a portfolio of farms.
- **Climate research.** Reanalysis back to 1940 without a vendor contract or per-call billing.
- **Energy and demand modelling.** Hourly temperature and cloud cover as model features.
- **Travel and events.** A 16-day outlook for a list of destinations, refreshed daily.

### Quick start

Three locations, three-day forecast with the defaults:

```json
{
 "locations": ["52.52,13.41", "Tokyo", "-33.8688,151.2093"]
}
```

Hourly modelling features, aligned to UTC, two weeks out:

```json
{
 "locations": ["40.7128,-74.0060", "51.5072,-0.1276"],
 "variables": ["temperature_2m", "precipitation", "cloud_cover", "wind_speed_10m"],
 "dailyVariables": ["temperature_2m_max", "temperature_2m_min", "precipitation_sum"],
 "forecastDays": 14,
 "timezone": "UTC",
 "units": "metric"
}
```

A decade of daily history for a backtest:

```json
{
 "locations": ["Berlin", "Paris", "Madrid"],
 "variables": [],
 "dailyVariables": ["temperature_2m_max", "temperature_2m_min", "precipitation_sum", "sunshine_duration"],
 "historicalFrom": "2015-01-01",
 "historicalTo": "2024-12-31",
 "timezone": "UTC"
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `locations` | array | required | `"lat,lon"` strings or place names. Place names are geocoded to the single best match. |
| `variables` | array | `temperature_2m`, `relative_humidity_2m`, `precipitation`, `wind_speed_10m` | Hourly variable names. An empty array means no hourly series. |
| `dailyVariables` | array | `weather_code`, `temperature_2m_max`, `temperature_2m_min`, `precipitation_sum` | Daily aggregates. An empty array means no daily series. |
| `forecastDays` | integer | `3` | Anything from 1 to 16. Ignored in historical mode. |
| `historicalFrom` | string | `""` | `YYYY-MM-DD`. Setting it switches to the archive API. |
| `historicalTo` | string | `""` | `YYYY-MM-DD`. Defaults to yesterday. |
| `timezone` | string | `auto` | IANA name, or `auto` for each location's local time. |
| `units` | string | `metric` | `metric` or `imperial`. |

Hourly and daily variables use different names: `temperature_2m` is hourly, `temperature_2m_max` is daily. Passing a daily name in `variables` makes Open-Meteo reject the whole request, and the Actor reports its `reason` verbatim in `RUN_SUMMARY.failures`. The full variable list lives in the [Open-Meteo docs](https://open-meteo.com/en/docs).

### Output example

```json
{
 "location": "Tokyo",
 "resolvedName": "Tokyo",
 "admin1": "Tokyo",
 "country": "Japan",
 "countryCode": "JP",
 "latitude": 35.7,
 "longitude": 139.6875,
 "elevation": 40,
 "timezone": "Asia/Tokyo",
 "timezoneAbbreviation": "GMT+9",
 "utcOffsetSeconds": 32400,
 "mode": "forecast",
 "currentTime": "2026-08-06T20:30",
 "currentTemperature": 27.1,
 "currentApparentTemperature": 33.6,
 "currentRelativeHumidity": 91,
 "currentPrecipitation": 0,
 "currentWindSpeed": 3.3,
 "currentWindDirection": 139,
 "currentWeatherCode": 1,
 "currentWeatherDescription": "Mainly clear",
 "hourly": {
 "time": ["2026-08-06T00:00", "2026-08-06T01:00", "2026-08-06T02:00"],
 "temperature_2m": [23.8, 23.3, 23.0],
 "precipitation": [0.2, 0.3, 0.3]
 },
 "daily": {
 "time": ["2026-08-06", "2026-08-07"],
 "weather_code": [51, 53],
 "temperature_2m_max": [31.1, 30.7],
 "temperature_2m_min": [22.6, 24.7],
 "precipitation_sum": [2.0, 3.1]
 },
 "historicalSeries": null,
 "units": {
 "current": { "temperature_2m": "°C", "wind_speed_10m": "km/h" },
 "hourly": { "time": "iso8601", "temperature_2m": "°C", "precipitation": "mm" },
 "daily": { "time": "iso8601", "temperature_2m_max": "°C", "precipitation_sum": "mm" }
 },
 "sourceUrl": "https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.69171&timezone=auto&forecast_days=2&current=.",
 "scrapedAt": "2026-08-06T11:39:49.134Z"
}
```

In historical mode `hourly`, `daily` and every `current*` field are null, and the series arrive under `historicalSeries.hourly` and `historicalSeries.daily` instead.

### Choosing variables

Series come back column-oriented, not row-oriented: one `time` array and one array per variable, all the same length. Index `i` of every array describes the same instant. To flatten to rows in JavaScript:

```js
const rows = record.daily.time.map((t, i) => ({
 date: t,
 max: record.daily.temperature_2m_max[i],
 min: record.daily.temperature_2m_min[i],
 rain: record.daily.precipitation_sum[i],
}));
```

Useful hourly names: `temperature_2m`, `apparent_temperature`, `precipitation`, `rain`, `snowfall`, `cloud_cover`, `wind_speed_10m`, `wind_gusts_10m`, `surface_pressure`, `soil_temperature_0cm`, `soil_moisture_0_to_7cm`.

Useful daily names: `weather_code`, `temperature_2m_max`, `temperature_2m_min`, `precipitation_sum`, `rain_sum`, `snowfall_sum`, `precipitation_hours`, `sunrise`, `sunset`, `sunshine_duration`, `wind_speed_10m_max`, `shortwave_radiation_sum`.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~open-meteo-weather-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "locations": ["52.52,13.41", "Tokyo"],
 "dailyVariables": ["temperature_2m_max", "precipitation_sum"],
 "forecastDays": 7,
 "timezone": "UTC"
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/open-meteo-weather-scraper').call({
 locations: ['Berlin', 'Paris'],
 historicalFrom: '2024-01-01',
 historicalTo: '2024-01-31',
 dailyVariables: ['temperature_2m_max', 'precipitation_sum'],
 variables: [],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const r of items) {
 const d = r.historicalSeries.daily;
 console.log(`${r.location}: ${d.time.length} days, hottest ${Math.max(.d.temperature_2m_max)}`);
}
```

### Limits and behaviour

- Two APIs sit behind one Actor. `historicalFrom` routes the request to `archive-api.open-meteo.com`, and without it you get `api.open-meteo.com/v1/forecast`. The archive rejects a start date without an end date, so an omitted `historicalTo` becomes yesterday.
- Coordinates snap to a grid. The returned `latitude` and `longitude` are the model cell centre, typically within a few kilometres of what you asked for. `elevation` is that cell's elevation, which matters when comparing mountain and valley readings.
- Bad variable names fail loudly. Open-Meteo answers HTTP 400 with a `reason` string, that reason is copied into `RUN_SUMMARY.failures`, and the run continues with the other locations.
- Transient errors get retried. 429s and 5xx responses get three attempts with linear backoff.
- Each location is one request. Locations are fetched sequentially, so a hundred-location run is a hundred requests.

### FAQ

**Do I need an API key?** No. You supply no credentials.

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**How far back does the history go?** To 1 January 1940, from ERA5 reanalysis. Recent days lag by a few days, which is why `historicalTo` defaults to yesterday rather than today.

**How is this different from the NWS Scraper?** NWS is the authoritative US government source and stops at the US border. Open-Meteo is global and adds 80+ years of history, but it is a model reanalysis rather than an official forecast product. Many users run both.

**Why is `currentTemperature` null?** You are in historical mode. The archive API has no concept of "now", so read `historicalSeries` instead.

**Can I schedule it?** Yes. There is no quota for reasonable non-commercial volumes, so daily or hourly refreshes are fine. Open-Meteo asks heavy commercial users to move to their paid plan.

# Actor input Schema

## `locations` (type: `array`):

Coordinates as "lat,lon" or place names to geocode ("Berlin", "Tokyo", "Sao Paulo"). Place names resolve to the best match from Open-Meteo's geocoder, so pass coordinates when you need an exact point.

## `variables` (type: `array`):

Hourly weather variables to request, using Open-Meteo names (temperature\_2m, precipitation, wind\_speed\_10m, cloud\_cover, soil\_moisture\_0\_to\_7cm …). Leave empty to skip the hourly series entirely.

## `dailyVariables` (type: `array`):

Daily aggregate variables (temperature\_2m\_max, precipitation\_sum, sunrise, sunset, wind\_speed\_10m\_max …). Daily names differ from hourly ones. Leave empty to skip the daily series.

## `forecastDays` (type: `integer`):

How many days of forecast to return, 1 to 16. Ignored when a historical start date is set.

## `historicalFrom` (type: `string`):

YYYY-MM-DD. Setting this switches the Actor to the reanalysis archive, which covers 1940 to a few days ago. Leave empty for a live forecast.

## `historicalTo` (type: `string`):

YYYY-MM-DD. Only used with a historical start date. Defaults to yesterday when omitted, because the archive requires an explicit end date.

## `timezone` (type: `string`):

IANA timezone for the returned timestamps, for example Europe/Berlin. The default 'auto' uses each location's own local time; pass UTC to align series across locations.

## `units` (type: `string`):

'metric' returns Celsius, km/h and millimetres; 'imperial' returns Fahrenheit, mph and inches. The exact unit for every variable is echoed back in the record's units object.

## Actor input object example

```json
{
  "locations": [
    "-33.8688,151.2093",
    "Reykjavik"
  ],
  "variables": [
    "temperature_2m",
    "precipitation",
    "cloud_cover"
  ],
  "dailyVariables": [
    "temperature_2m_max",
    "precipitation_sum",
    "sunrise",
    "sunset"
  ],
  "forecastDays": 3,
  "historicalFrom": "2024-01-01",
  "historicalTo": "2024-12-31",
  "timezone": "UTC",
  "units": "metric"
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "locations": [
        "52.52,13.41",
        "Tokyo"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/open-meteo-weather-scraper").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 = { "locations": [
        "52.52,13.41",
        "Tokyo",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/open-meteo-weather-scraper").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 '{
  "locations": [
    "52.52,13.41",
    "Tokyo"
  ]
}' |
apify call arman-bd/open-meteo-weather-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/open-meteo-weather-scraper"
        }
    }
}

```

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/6LqVkTcHEimwxXGlg/builds/OjY64nsdK7s341Rvm/openapi.json
