# Drone No-Fly Alerts - New FAA TFRs Near Your Locations (`that_red_bird/faa-tfr-geofence-alerts`) Actor

Flying a drone job next week? Give it your job sites (lat/lng + radius) and get an alert the moment a NEW FAA Temporary Flight Restriction (VIP, stadium, hazard, security) touches one. Reads the official FAA TFR list and geometry; each TFR is reported once.

- **URL**: https://apify.com/that_red_bird/faa-tfr-geofence-alerts.md
- **Developed by:** [mohamed alaya](https://apify.com/that_red_bird) (community)
- **Categories:** Automation, News, Other
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 tfrs

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

## Drone No-Fly Alerts - New FAA TFRs Near Your Locations

Get told when a **new FAA Temporary Flight Restriction (TFR)** touches one of your drone job sites. Built for drone service companies, Part 107 pilots and event or film crews who need to know before they fly.

It reads the FAA's official active TFR list and each TFR's published geometry, remembers what it already told you, and flags only the new TFRs that cover (or come near) your points.

### Quick start - three copy-paste inputs

Demo (empty input works):

```json
{}
```

Two job sites with their own radius:

```json
{
  "locations": [
    { "name": "Stadium shoot", "lat": 39.2904, "lng": -76.6122, "radiusMiles": 10 },
    "34.0522,-118.2437"
  ],
  "radiusMiles": 5,
  "outputMode": "hits-only",
  "stateStoreName": "my-drone-company-tfrs"
}
```

Only VIP and security TFRs in Florida and Texas, with a Slack webhook:

```json
{
  "locations": [{ "name": "Orlando job", "lat": 28.5384, "lng": -81.3789 }],
  "tfrTypes": ["VIP", "SECURITY"],
  "states": ["FL", "TX"],
  "webhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ"
}
```

Schedule it hourly or daily with the same `stateStoreName` and you get an alert only for TFRs you have not seen.

### Input guide

| Field | What it does |
|---|---|
| `locations` | Your points: objects `{name, lat, lng, radiusMiles}` or plain `"lat,lng"` text. |
| `radiusMiles` | A TFR touches a point when its boundary is within this many miles (0 = point must be inside). |
| `tfrTypes`, `states` | Optional filters (VIP, SECURITY, HAZARDS, SPACE OPERATIONS, AIR SHOWS/SPORTS, UAS PUBLIC GATHERING ...). |
| `outputMode` | `all-new` = a row for every new TFR plus every hit; `hits-only` = only TFRs that touch your points. |
| `maxBaselineRows` | On the first run every active TFR looks new; at most this many TFR rows are listed (marked `isBaseline`). Hits are still checked for all of them. |
| `stateStoreName` | Named memory store. Same name on every scheduled run. |
| `webhookUrl` | Optional POST of new hits (Slack, Discord, Zapier, Make). |

### Output

| Field | Meaning |
|---|---|
| `type` | `tfr`, `geofence-hit`, `error` or `run-summary` |
| `notamId`, `tfrType`, `state` | FAA NOTAM number, type and state |
| `location`, `distanceMiles`, `inside` | (hit rows) your point and how far the TFR boundary is (0 = inside) |
| `effectiveFrom`, `effectiveUntil`, `activeNow` | Schedule in UTC; `effectiveUntil` null means no end date published |
| `floorFt`, `ceilingFt` | Altitude limits from the FAA data |
| `url` | The FAA's TFR detail file (XML) |

Sample hit row:

```json
{"type":"geofence-hit","notamId":"6/7096","tfrType":"VIP","location":"Baltimore job site","inside":true,"distanceMiles":0,"effectiveFrom":"2026-10-06T18:45:00Z","effectiveUntil":"2026-10-06T22:45:00Z","message":"Baltimore job site is INSIDE VIP TFR 6/7096."}
```

### Use it with

- Schedule + webhook to Slack for a daily "any new TFR near my jobs" ping.
- Feed `geofence-hit` rows into your dispatch sheet or CRM through Make or Zapier.

### FAQ

**Is this the legal source?** It uses the FAA's public TFR data (tfr.faa.gov). Always confirm in the FAA's own tools and your flight planning app before you fly. This Actor is an alerting aid, not flight clearance.

**Does it cover LAANC, NOTAMs other than TFRs, or controlled airspace?** No. TFRs only.

**Why are some TFRs permanent?** Theme parks and similar sites have long-running TFRs; they have no end date.

### Honest limits

- Polygon edges are used as published (circles are already sampled into polygons by the FAA). Distances are approximate (flat-earth math, good to a fraction of a mile).
- A TFR is reported once, by NOTAM number. A later amendment of the same NOTAM is not re-reported.
- If the FAA's geometry file for a TFR cannot be read, that TFR is retried on the next run and an `error` row says so.

### Price

Pay per event: **$0.002 per new TFR row**, **$0.01 per geofence hit**. A daily run with two job sites that sees 8 new TFRs and 1 hit costs about $0.026.

# Actor input Schema

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

Points to protect. Each item is an object {"name":"Job site A","lat":38.8977,"lng":-77.0365,"radiusMiles":5} (radiusMiles optional, falls back to the default radius) or a plain "lat,lng" text. Use decimal degrees.

## `radiusMiles` (type: `integer`):

A TFR counts as touching a location when its boundary is within this many statute miles of the point (0 = the point must be inside the TFR). Individual locations can override it.

## `tfrTypes` (type: `array`):

Only keep these FAA TFR types, for example VIP, SECURITY, HAZARDS, SPACE OPERATIONS, AIR SHOWS/SPORTS, UAS PUBLIC GATHERING. Matching is case-insensitive. Leave empty to keep every type.

## `states` (type: `array`):

Optional two-letter state codes (for example CA, TX). Only TFRs listed under these states are processed. Leave empty for all states.

## `outputMode` (type: `string`):

all-new: a row for every new TFR plus a row for every geofence hit. hits-only: only the rows for TFRs that touch your locations (cheapest for scheduled alerts).

## `maxBaselineRows` (type: `integer`):

The first run has no memory, so every active TFR looks new. It lists at most this many TFR rows (newest first, marked isBaseline) and still checks all of them against your locations.

## `stateStoreName` (type: `string`):

Named key-value store that remembers which TFRs were already reported. Use the same name on every scheduled run and a different name per project. Empty = no memory (every run reports everything).

## `webhookUrl` (type: `string`):

New geofence hits are POSTed here as JSON with text and content fields (works with Slack, Discord, Zapier, Make). Leave empty for none.

## Actor input object example

```json
{
  "locations": [
    {
      "name": "Walt Disney World, Orlando FL",
      "lat": 28.4177,
      "lng": -81.5812
    },
    {
      "name": "Disneyland, Anaheim CA",
      "lat": 33.8121,
      "lng": -117.919
    }
  ],
  "radiusMiles": 5,
  "tfrTypes": [],
  "states": [],
  "outputMode": "all-new",
  "maxBaselineRows": 10,
  "stateStoreName": "faa-tfr-geofence-state",
  "webhookUrl": ""
}
```

# Actor output Schema

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

No description

## `downloadCsv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `count` (type: `string`):

No description

# 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("that_red_bird/faa-tfr-geofence-alerts").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("that_red_bird/faa-tfr-geofence-alerts").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 that_red_bird/faa-tfr-geofence-alerts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,that_red_bird/faa-tfr-geofence-alerts"
        }
    }
}
```

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/FGDW4UMOeuRFs3ifQ/builds/IfjIJsHci9zpdWjiQ/openapi.json
