# Sunrise & Sunset Scraper: Solar Times by Coordinate (`arman-bd/sunrise-sunset-scraper`) Actor

Calculate sunrise, sunset, solar noon, civil/nautical/astronomical twilight and day length for any coordinate and date. No login, no proxy, no browser.

- **URL**: https://apify.com/arman-bd/sunrise-sunset-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Automation, AI, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 time 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/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

## Sunrise & Sunset Scraper: Solar Times by Coordinate

![Sunrise & Sunset Scraper: Sunrise, sunset, solar noon, day length and civil/nautical/astronomical twilight for any coordinate and date](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/sunrise-sunset-scraper.jpg)

**Sunrise & Sunset Scraper** computes sunrise, sunset, solar noon, day length and all three twilight bands, civil, nautical and astronomical, for any coordinate on Earth and any date.

It reads the public **Sunrise-Sunset API** directly: **no proxy setup, no browser, no credentials to manage.** A year of golden-hour times for one city is a run measured in seconds.

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

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/sunrise-sunset-scraper.md
```

### What you get

| Output field | Meaning |
|---|---|
| `location` | Your own label if you gave one, otherwise the canonical `lat,lon` string |
| `latitude`, `longitude` | Parsed and range-validated coordinates |
| `date` | The day computed, `YYYY-MM-DD` |
| `sunrise`, `sunset` | ISO 8601 timestamps in your chosen timezone |
| `solarNoon` | Moment the sun is highest |
| `dayLength` | Seconds between sunrise and sunset |
| `civilTwilightBegin`, `civilTwilightEnd` | Dawn and dusk, sun 6° below the horizon |
| `nauticalTwilightBegin`, `nauticalTwilightEnd` | Sun 12° below the horizon |
| `astronomicalTwilightBegin`, `astronomicalTwilightEnd` | Sun 18° below the horizon, full darkness |
| `scrapedAt` | Run timestamp |

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

### Common use cases

- **Photography and film scheduling.** Plan shoots around golden hour and blue hour across a whole location scout list.
- **Lighting and energy automation.** Drive smart-home or building schedules off real sunset times instead of fixed clocks.
- **Agriculture and field work.** Compute usable daylight per site, per day, across a season.
- **Astronomy planning.** Astronomical twilight tells you when the sky is actually dark.
- **AI agents and enrichment pipelines.** A cheap, deterministic daylight layer for any coordinate you already hold.

### Quick start

Two cities, today:

```json
{
 "locations": ["Malaga: 36.72,-4.42", "Reykjavik: 64.15,-21.94"]
}
```

A week of golden hour in local time:

```json
{
 "locations": ["Malaga: 36.72,-4.42"],
 "dates": ["2026-08-01.2026-08-07"],
 "timezone": "Europe/Madrid"
}
```

Solstice comparison across a shortlist, capped:

```json
{
 "locations": [
 "Quito: -0.18,-78.47",
 "London: 51.51,-0.13",
 "Tromso: 69.65,18.96"
 ],
 "dates": ["2026-06-21", "2026-12-21"],
 "timezone": "UTC",
 "maxResults": 100
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `locations` | array | - | **Required.** `lat,lon` strings. Prefix with `Name:` to label the output. Google Maps URLs and `{ name, lat, lng }` objects are accepted too. |
| `dates` | array | `[today]` | `YYYY-MM-DD`, the keywords `today` / `tomorrow` / `yesterday`, or an inclusive range `2026-08-01.2026-08-07`. |
| `timezone` | string | `UTC` | IANA name, e.g. `Europe/Madrid`. Applies to every returned timestamp. |
| `maxResults` | integer | `0` | Cap on total records. `0` = no limit. |

`locations` and `dates` multiply out: 10 locations × 365 dates is 3,650 records and 3,650 requests. Use `maxResults` when experimenting.

### Output example

```json
{
 "location": "Malaga",
 "latitude": 36.72,
 "longitude": -4.42,
 "date": "2026-08-06",
 "sunrise": "2026-08-06T07:26:18+02:00",
 "sunset": "2026-08-06T21:20:52+02:00",
 "solarNoon": "2026-08-06T14:23:35+02:00",
 "dayLength": 50074,
 "civilTwilightBegin": "2026-08-06T06:59:33+02:00",
 "civilTwilightEnd": "2026-08-06T21:47:37+02:00",
 "nauticalTwilightBegin": "2026-08-06T06:25:25+02:00",
 "nauticalTwilightEnd": "2026-08-06T22:21:46+02:00",
 "astronomicalTwilightBegin": "2026-08-06T05:48:56+02:00",
 "astronomicalTwilightEnd": "2026-08-06T22:58:14+02:00",
 "scrapedAt": "2026-08-06T12:00:00.000Z"
}
```

`dayLength` is in seconds, 50,074 s is 13 h 54 m 34 s.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~sunrise-sunset-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "locations": ["Malaga: 36.72,-4.42"],
 "dates": ["2026-08-01.2026-08-07"],
 "timezone": "Europe/Madrid"
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/sunrise-sunset-scraper').call({
 locations: ['Malaga: 36.72,-4.42', 'Reykjavik: 64.15,-21.94'],
 dates: ['2026-08-01.2026-08-07'],
 timezone: 'UTC',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const d of items) console.log(`${d.location} ${d.date}: sunset ${d.sunset}`);
```

### Notes

- **Polar day and polar night return `null`, not 1970.** Above the Arctic and below the Antarctic Circle the source reports a Unix-epoch sentinel (`1970-01-01T00:00:00+00:00`) where a sunrise would be. That silently breaks sorting and date arithmetic, so this Actor converts it to `null`. When `dayLength` is `0` and `sunrise` is `null`, the sun did not rise or did not set that day.
- **Coordinates are validated here, not at the source.** The API answers `lat=999` with HTTP 200 and meaningless times. Out-of-range and unparseable entries are dropped before any request is made and listed in `RUN_SUMMARY.locationsRejected`.
- **One bad location-date doesn't kill the run.** Failures are recorded in `RUN_SUMMARY.failures`; the Actor only errors out if every pair failed.
- **Transient errors are retried.** 429, 5xx and network errors get four attempts with exponential backoff (0.5 s → 1 s → 2 s). Malformed requests fail immediately rather than burning retries.
- **Public data only.** No authentication, no personal data, no access-control bypass.

### FAQ

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

**Do I need an account on the source?** No, and there is no credentials to manage.

**What happens if the source is unavailable?** The affected location-date is reported in `RUN_SUMMARY.failures` and the run continues with the rest.

**Can I schedule it?** Yes, it is designed for scheduled runs. A nightly run over your location list keeps a rolling daylight table warm.

**Why is `dayLength` a number instead of `13:54:34`?** Seconds sort and subtract correctly in every tool. Divide by 3600 for hours.

**Is `sunset` the same as golden hour?** Golden hour is roughly the hour before `sunset` and after `sunrise`; blue hour sits between `sunset` and `civilTwilightEnd`. Both are derivable from the fields returned.

**Does the timezone affect the calculation?** No, only the formatting. Solar times are computed from the coordinate; `timezone` decides which UTC offset they are printed in.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

# Actor input Schema

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

Coordinates as 'lat,lon'. Add a label with 'Name: lat,lon' to keep your own naming in the output. Google Maps URLs are accepted and parsed automatically.

## `dates` (type: `array`):

Dates to compute as YYYY-MM-DD. Also accepts 'today', 'tomorrow', 'yesterday' and inclusive ranges like '2026-08-01.2026-08-07'. Leave empty for today.

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

IANA timezone name used for the returned timestamps, e.g. 'Europe/Madrid'. Defaults to UTC. Unrecognised names silently fall back to UTC at the source.

## `maxResults` (type: `integer`):

Cap the total number of location-date records saved. Set 0 for no limit. Useful because locations and dates multiply out.

## Actor input object example

```json
{
  "locations": [
    "36.72,-4.42",
    "https://www.google.com/maps/@51.5074,-0.1278,12z"
  ],
  "dates": [
    "2026-08-06",
    "2026-12-21.2026-12-24"
  ],
  "timezone": "Europe/Madrid",
  "maxResults": 0
}
```

# 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": [
        "Malaga: 36.72,-4.42",
        "Reykjavik: 64.15,-21.94"
    ],
    "timezone": "UTC"
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/sunrise-sunset-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": [
        "Malaga: 36.72,-4.42",
        "Reykjavik: 64.15,-21.94",
    ],
    "timezone": "UTC",
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/sunrise-sunset-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": [
    "Malaga: 36.72,-4.42",
    "Reykjavik: 64.15,-21.94"
  ],
  "timezone": "UTC"
}' |
apify call arman-bd/sunrise-sunset-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/sunrise-sunset-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/N3D6TSOS5ObN39a95/builds/aTjVH6joqwd8VlhEQ/openapi.json
