# iNaturalist Observations Scraper - Species, Photos & GPS (`ninhothedev/inaturalist-scraper`) Actor

$0.3/1K 🔥 iNaturalist scraper! Wildlife observations with species, photos, coordinates & observer. No key. JSON, CSV, Excel or API in seconds. Build ecology & ML training datasets ⚡

- **URL**: https://apify.com/ninhothedev/inaturalist-scraper.md
- **Developed by:** [ninhothedev](https://apify.com/ninhothedev) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 results

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/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

## iNaturalist Observations Scraper - Wildlife Data with Species, Photos & GPS

Scrape **wildlife observations from iNaturalist** by search query and get clean, structured biodiversity records: scientific species name, common name, taxonomic rank, iconic taxon, observation date, quality grade, place name, **GPS latitude/longitude**, observer, photo URL with attribution, identification and comment counts, license and permalink.

**No API key. No login. No proxy required.** The actor talks to the public iNaturalist API v1 directly and runs fine on plain datacenter IPs, so a run costs you essentially just compute.

***

### What this iNaturalist scraper does

iNaturalist is the world's largest citizen-science platform for nature observations - hundreds of millions of records of plants, insects, birds, fungi, mammals and reptiles, each with a photo, a location and a community-verified identification. This actor turns any search query into an analysis-ready dataset.

Give it a query like `monarch butterfly`, `Danaus plexippus`, `Quercus` or `red fox`, and it returns one row per observation, deduplicated across queries, ready to export as JSON, CSV or Excel.

#### Features

- **Search by anything** - common names, scientific names, genera, families or place words.
- **Real GPS coordinates** - latitude/longitude resolved from the API's `geojson` payload (obscured records for threatened species correctly return `null` instead of a fake point).
- **Photos with attribution** - direct photo URL plus the CC attribution string, so you stay license-compliant.
- **Species-level taxonomy** - scientific name, common name, rank and iconic taxon (Insecta, Aves, Plantae, Mammalia ...).
- **Data-quality signals** - `quality_grade` (`research` / `needs_id` / `casual`), identification count and comment count let you filter down to research-grade records only.
- **Multi-query runs** - pass a whole list of species and get one merged, deduplicated dataset.
- **Permissive output schema** - never fails a run because a field arrived as a different type.

***

### Pricing

Roughly **$0.50 per 1,000 observations** in Apify platform usage. There are no third-party API fees, no proxy costs and no subscription - iNaturalist's API is free and open.

***

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `observations` | What to scrape. Currently `observations` - wildlife records with species, photo, location and observer. |
| `queries` | array | `["monarch butterfly", "red fox"]` | Free-text search terms. Common names, scientific names, genera or places. Each is scraped separately, duplicates removed. |
| `maxItems` | integer | `100` | Max observations to push across all queries. Max `1000` per run. |

#### Example input

```json
{
  "mode": "observations",
  "queries": ["monarch butterfly", "Danaus plexippus", "red fox"],
  "maxItems": 500
}
```

***

### Output

One dataset item per observation. Every field is nullable.

```json
{
  "id": 385922230,
  "species": "Danaus plexippus",
  "common_name": "Monarch",
  "rank": "species",
  "iconic_taxon": "Insecta",
  "observed_on": "2026-07-28T00:00:00",
  "created_at": "2026-07-28T07:32:43-04:00",
  "quality_grade": "needs_id",
  "description": null,
  "place": "Ohio, US",
  "latitude": 40.4612448671,
  "longitude": -81.4414084526,
  "observer": "jonesy5",
  "observer_name": null,
  "photo_url": "https://inaturalist-open-data.s3.amazonaws.com/photos/706562729/square.jpg",
  "photo_attribution": "(c) jonesy5, some rights reserved (CC BY-NC)",
  "identifications_count": 0,
  "comments_count": 0,
  "license": "cc-by-nc",
  "url": "https://www.inaturalist.org/observations/385922230",
  "source": "inaturalist",
  "scraped_at": "2026-07-28T11:38:43.755205+00:00"
}
```

#### Field reference

| Field | Meaning |
|---|---|
| `id` | iNaturalist observation ID |
| `species` | Scientific name of the identified taxon |
| `common_name` | Preferred common name |
| `rank` | Taxonomic rank (`species`, `genus`, `family` ...) |
| `iconic_taxon` | High-level group (`Insecta`, `Aves`, `Plantae`, `Mammalia` ...) |
| `observed_on` | When the organism was observed (ISO-8601) |
| `created_at` | When the record was uploaded (ISO-8601) |
| `quality_grade` | `research`, `needs_id` or `casual` |
| `description` | Observer's notes, capped at 2,000 characters |
| `place` | Human-readable place guess |
| `latitude` / `longitude` | Decimal GPS coordinates, `null` when obscured |
| `observer` / `observer_name` | Contributor login and display name |
| `photo_url` / `photo_attribution` | First photo and its CC attribution |
| `identifications_count` | Number of community identifications |
| `comments_count` | Number of comments |
| `license` | Observation license code (e.g. `cc-by-nc`) |
| `url` | Permalink to the observation |
| `source` | Always `inaturalist` |
| `scraped_at` | Run timestamp (UTC, ISO-8601) |

***

### Use cases

- **Biodiversity monitoring** - track where a species is being recorded and how sightings shift across seasons and years.
- **Citizen science** - pull community-contributed records for a region, a bioblitz or a school project without touching the API yourself.
- **Ecology datasets** - build species occurrence tables with coordinates for range mapping, habitat modelling and invasive-species tracking.
- **ML training data** - harvest labelled wildlife photos with species names, ranks and licenses for computer-vision datasets.
- **Conservation reporting** - filter to `quality_grade = research` for verified records suitable for publications and grant reports.
- **Journalism & content** - illustrate stories about local wildlife with properly attributed, openly licensed photos.

***

### Notes & limits

- iNaturalist caps deep pagination at **10,000 results per query** (`page × per_page`). For bigger harvests, split your search into narrower queries.
- Coordinates for threatened or user-obscured observations are intentionally withheld by iNaturalist and come back as `null`.
- Photos are user-contributed and licensed individually - always respect the `license` and `photo_attribution` fields when republishing.

***

### Related actors

- [GBIF Scraper](https://apify.com/ninhothedev/gbif-scraper) - global biodiversity occurrence records from the GBIF network.
- [Earthquake Scraper](https://apify.com/ninhothedev/earthquake-scraper) - real-time seismic event data.
- [Air Quality Scraper](https://apify.com/ninhothedev/air-quality-scraper) - air pollution and AQI measurements by location.
- [Openverse Media Scraper](https://apify.com/ninhothedev/openverse-media-scraper) - openly licensed images and audio.

***

### Data source & attribution

Data comes from the public [iNaturalist API v1](https://api.inaturalist.org/v1/docs/). Observations and photos belong to their individual contributors under the licenses reported in each record. This actor is not affiliated with or endorsed by iNaturalist or the California Academy of Sciences.

# Actor input Schema

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

What to scrape. 'observations' searches iNaturalist for wildlife observations matching your queries and returns one dataset item per observation (species, photo, GPS location and observer).

## `queries` (type: `array`):

Free-text search terms sent to the iNaturalist observation search. Use common names ('monarch butterfly'), scientific names ('Danaus plexippus'), genera ('Quercus') or places ('Yosemite'). Each query is scraped separately and duplicate observations are removed.

## `maxItems` (type: `integer`):

Maximum number of observations to push to the dataset across all queries. Keep it low for quick test runs; raise it for full biodiversity exports. Hard limit is 1000 per run.

## Actor input object example

```json
{
  "mode": "observations",
  "queries": [
    "monarch butterfly",
    "red fox"
  ],
  "maxItems": 100
}
```

# 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 = {
    "queries": [
        "monarch butterfly",
        "red fox"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ninhothedev/inaturalist-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 = { "queries": [
        "monarch butterfly",
        "red fox",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ninhothedev/inaturalist-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 '{
  "queries": [
    "monarch butterfly",
    "red fox"
  ]
}' |
apify call ninhothedev/inaturalist-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ninhothedev/inaturalist-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/RZi1Mga8DZC5BkrEl/builds/9dAGTnXaco6JMe5m3/openapi.json
