# GTFS Service-Date Report (`l3digital/gtfs-service-date-report`) Actor

Experimental bounded scheduled-service report for an explicit GTFS service date and stop in a supplied public feed.

- **URL**: https://apify.com/l3digital/gtfs-service-date-report.md
- **Developed by:** [L3Digital](https://apify.com/l3digital) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.05 / complete gtfs service-date report

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## GTFS Service-Date Report

Query scheduled service at one exact `stop_id` in one caller-authorized public
GTFS Schedule ZIP for one explicit service date. The report retains source
identifiers, date coverage, agency timezone, timing qualifiers and boarding
constraints. It returns compressed frequency windows rather than expanding
thousands of departures. This experimental TRYOUT tests convenience for callers
without an installed GTFS query runtime; demand and comparative savings are unknown.

### Use from an AI agent through MCP

Add this URL to a client that supports remote HTTP MCP, then authorize with your
own Apify account using the [Apify MCP setup guide](https://docs.apify.com/integrations/mcp):

```text
https://mcp.apify.com/?tools=l3digital/gtfs-service-date-report
```

This configuration selects the Actor directly. Availability still depends on
Apify account and Actor eligibility. Call `l3digital/gtfs-service-date-report`
with the example input below; the pricing and input restrictions on this page apply.

When using `call-actor`, its response contains run status and storage IDs. If the
run is still active, check that run with `get-actor-run`. After success, retrieve
the report with `get-dataset-items` using the returned dataset ID
(`defaultDatasetId` in the run API). These retrieval tools load with the Actor.
Retrieve the existing result instead of starting another run. Inspect the report
status and the coverage or refusal fields described below before using its values.

### Input

The default `{}` runs a deterministic illustrative demo, makes no upstream
request and is never eligible for a useful-report event. Report mode requires:

```json
{"mode":"report","feedUrl":"https://example.org/authorized-feed.zip","serviceDate":"2026-01-05","stopId":"S"}
```

This URL is illustrative. Supply a public URL that you are authorized to use.
The URL must serve the ZIP directly, without redirects, authentication or an
upstream account. International hostnames are accepted when IDNA encoding is
valid; non-ASCII URL path and query characters must be percent-encoded.
`serviceDate` must be a real ISO calendar date. `stopId` is
matched exactly, including whitespace and case. No station-child expansion or
weekday fallback occurs. Unknown fields and mismatched demo/report fields are
refused before acquisition. The Console form describes these constraints;
runtime validates the conditional fields and UTF-8 byte lengths.
The hostname must resolve only to public unicast addresses. The connection is
pinned to a validated address, with the original hostname retained for HTTPS
certificate checks; private, loopback and link-local destinations are refused.

### Read the result

One dataset item contains `schemaVersion`, `mode`, `status`, `useful`,
`serviceDate`, `stopId`, `source`, `coverage`, `agencyTimezone`, `scheduledVisits`,
`frequencyWindows` and `diagnostics`. `complete` is useful only when the exact
stop exists, the date lies in source coverage and the declared subset was
processed within bounds. A complete result with zero visits means no scheduled
visits were found in this supplied source on that GTFS service date. It does
not establish whether vehicles physically run. Unknown stops and coverage gaps
are `incomplete`, never useful zero-service results. Other statuses are `demo`,
`unsupported` and `source_error`; each is unuseful.

Coverage uses a valid paired `feed_info` range when supplied; otherwise it
retains the union of weekly validity ranges and explicit exception dates,
including gaps. Weekly activation uses the actual date, then additions/removals
apply. Calendar-dates-only feeds are supported. Source provenance records the
requested URL, completed ZIP SHA-256, actual transferred bytes and member
hashes/row counts plus available publisher/version fields. Failed acquisition
has no completed ZIP hash.

Service-day offsets preserve times above 24 hours on the requested date.
`timepoint=0` is approximate, `1` is source exact and omitted/empty is source
default exact. These qualifiers never guarantee actual arrival. Agency timezone
is preserved; there is no absolute UTC conversion claim. Repeated stop visits
retain their sequence. Pickup/drop-off restrictions remain visible and do not
promise boardability.

Frequency schedules use the first-stop departure as origin. Every queried visit
has separate arrival/departure template offsets and shifted window endpoints.
`exact_times=1` returns a compressed fixed schedule and an end-exclusive count;
`0` or empty returns a headway window with no invented exact count or instants.
The template is never also emitted as a fixed departure.

### Supported subset and limits

Required tables are `agency`, `stops`, `routes`, `trips`, `stop_times` and at least
one of `calendar` or `calendar_dates`; `feed_info` and `frequencies` are optional.
Root-level UTF-8 CSV with optional BOM is supported. Missing relevant times,
broken joins, conflicting identities/sequences/calendars and invalid timezone
context cannot produce useful results. Flexible location/window forms, booking
rules, unknown boarding codes and continuous boarding forms outside the source
no-continuous form are explicitly unsupported. This is a schedule-query subset,
not full GTFS validation, live arrivals, routing, feed discovery or freshness
monitoring.

Each run issues one source request without retry or redirect. Bounds are 16 MiB
actual transfer, 32 distinct root entries, 64 MiB actual expanded bytes including
ignored members, 250,000 data rows, 50,000 trips, 64 columns, 64 KiB per UTF-8
field, 128 KiB per logical row, 1,000 combined visit/window records and 1 MiB
serialized output. MiB and KiB are binary units. Exceeding a bound produces an
unuseful outcome rather than silently truncating a complete report. Source work
runs in a process that is killed and joined at its deadline; run-owned temporary
files are cleaned. The total application deadline is 120 seconds, reserving
10 seconds after at most 110 seconds of source work. SDK setup and input time
consume that same budget; outer cancellation kills and joins the child before
returning. The report budget and child artifact use compact UTF-8 JSON, matching
the pinned platform dataset item serializer. The SDK adds a two-byte JSON array
wrapper around the single item; that transport framing is outside the per-report
1 MiB limit. Platform settings are 512 MiB and 180 seconds, no standby or restart.
These bounds are refusal limits, not a guarantee that every feed within them fits
platform memory or finishes before the deadline.

### Persistence and pricing

Every controlled terminal result persists once. Only a complete useful report
may attempt one configured `report-produced` event of count 1 afterward. Demo,
refusal, incomplete and source-error outputs emit none. Unpriced development
runs emit none and log the skip. Configured paid runs missing the event or event
capacity fail before source work. Storage and charge failures are not retried;
only an observed `charged_count=1` establishes accepted charging, logged after
persistence. Initial test pricing is $0.05 per complete useful report, including
a verified in-coverage zero-service result. Demo, refused, incomplete and
source-error results have no report-event charge. Owner QA does not establish
customer payment or settlement.

Choose the boarding stop's exact ID. Parent stations and entrances are not
expanded to their child stops: an existing parent or entrance ID with no direct
visits can produce a complete zero-visit report and incur the $0.05 event.

### Development and alternatives

From the repository root, run the four Actor-scoped gates in order:

```bash
rexec -- uv run --directory actors/gtfs-service-date-report --locked ruff format --check .
rexec -- uv run --directory actors/gtfs-service-date-report --locked ruff check .
rexec -- uv run --directory actors/gtfs-service-date-report --locked pyright
rexec -- uv run --directory actors/gtfs-service-date-report --locked pytest
```

Python 3.13, uv, Pydantic v2 and Apify SDK are independently locked. Native GTFS
libraries such as https://github.com/mrcagney/gtfs\_kit and hosted APIs such as
https://www.transit.land/documentation remain strong substitutes. Retained
validation covers synthetic schedules, offline faults and seven hosted owner-QA
cases. Those small hosted runs measured technical behavior and sample costs;
representative agency-feed compatibility, maximum-envelope performance,
commercial margin, customer demand and comparative savings remain unmeasured.

# Actor input Schema

## `mode` (type: `string`):

Demo is illustrative and upstream-free; report acquires one caller-authorized public ZIP.

## `feedUrl` (type: `string`):

Report only: public HTTP(S) URL, at most 2048 UTF-8 bytes, no credentials, fragment, redirect or login.

## `serviceDate` (type: `string`):

Report only: real ISO calendar date, e.g. 2026-01-05. No weekday fallback or UTC conversion.

## `stopId` (type: `string`):

Report only: exact original stop\_id, at most 256 UTF-8 bytes. No trimming, station expansion or case folding.

## Actor input object example

```json
{
  "mode": "demo"
}
```

# Actor output Schema

## `report` (type: `string`):

One source-bound terminal result, including explicit unuseful outcomes.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("l3digital/gtfs-service-date-report").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("l3digital/gtfs-service-date-report").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 '{}' |
apify call l3digital/gtfs-service-date-report --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,l3digital/gtfs-service-date-report"
        }
    }
}
```

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/R1nmLd5WvEHTIk2qp/builds/xfUByRzYRMWFQVUpU/openapi.json
