# WMS Capabilities Extractor (`datamule/wms-capabilities-extractor`) Actor

Point at any OGC Web Map Service (WMS 1.1.1 or 1.3.0) and extract a structured layer catalog from its GetCapabilities document — one row per layer with CRS, bounding boxes, styles, scale, keywords, dimensions and service metadata. One actor, any WMS.

- **URL**: https://apify.com/datamule/wms-capabilities-extractor.md
- **Developed by:** [Datamule](https://apify.com/datamule) (community)
- **Categories:** Developer tools, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 layers

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## WMS Capabilities Extractor

**Point at any OGC Web Map Service → get a clean, structured layer catalog.**

A generic runner over the **OGC Web Map Service (WMS) `GetCapabilities`** protocol
(versions **1.1.1** and **1.3.0**). WMS is *the* OGC standard for map and raster
services, so one actor spans an enormous population of endpoints with no per-site
scraper:

- national mapping agencies and every national Spatial Data Infrastructure (SDI)
- NOAA / USGS / NASA and other government geospatial portals
- the EU **INSPIRE** directive's view services
- terrestris / OSM-WMS and other basemap providers
- every **GeoServer**, **MapServer**, **QGIS Server**, **ArcGIS** and **MapProxy**
  deployment

Give it a WMS endpoint; it fetches the `GetCapabilities` XML, parses it, and flattens
it to one row **per advertised layer** with the fully-resolved metadata.

### What makes it different

The Apify Store has plenty of *per-site* map scrapers. This is a **protocol runner**:
one input (a WMS URL) works against *any* compliant service. It is the geospatial-map
sibling of the OGC API Features / STAC / OGC EDR / OGC API Records generic extractors.

**The parse is the moat.** WMS layers nest, and per the spec a child layer *inherits*
a defined set of its parent's properties. This runner walks the whole `<Layer>` tree
and correctly **accumulates inherited state** down each branch (CRS/SRS and styles are
added; geographic + per-CRS bounding boxes, attribution, scale bounds and dimensions
are replaced-or-inherited), then emits each named layer with its effective, merged
properties — not just the shallow top-level view.

Both WMS versions are handled from the same code by matching element **local names**,
so the namespaced 1.3.0 tree (`CRS`, `EX_GeographicBoundingBox`) and the un-namespaced
1.1.1 tree (`SRS`, `LatLonBoundingBox`, `ScaleHint`, `<Extent>`) both parse cleanly. The
reported `_wmsVersion` is read from the served document itself.

### Input

| Field | Type | Notes |
|---|---|---|
| `endpoint` | string | A WMS base URL or full GetCapabilities URL. Required unless `endpoints` is given. |
| `endpoints` | array | Optional. Harvest several WMS services in one run; each row is tagged with its `_endpoint`. |
| `version` | string | `1.3.0` (default) or `1.1.1`. A `VERSION` already in the URL wins. |
| `maxRecords` | integer | Optional global cap across all endpoints. |
| `userAgent` | string | Optional User-Agent override. |
| `timeoutSecs` | integer | Optional read timeout (default 60s). |
| `bearer` / `extraHeaders` | string / object | Optional, for auth-gated deployments. Never required, never logged. |

The `SERVICE=WMS&REQUEST=GetCapabilities&VERSION=...` parameters are appended for you
when absent, so pasting a bare service root works.

#### Example

```json
{
  "endpoint": "https://ows.terrestris.de/osm/service",
  "version": "1.3.0"
}
````

### Output

One row per advertised (named) layer:

- **Identity** — `name`, `title`, `abstract`, `queryable`, `opaque`, `cascaded`
- **Spatial** — `crs[]` (inherited + own, merged), `geographicBoundingBox`
  `{west,east,south,north}`, `boundingBoxes[]` (per-CRS, coordinates recorded verbatim
  and tagged with their CRS so the 1.3.0 EPSG:4326 axis order is unambiguous)
- **Cartography** — `styles[]` (`{name,title,legendUrl,...}`), `minScaleDenominator`,
  `maxScaleDenominator`, `scaleHintMin/Max` (1.1.1)
- **Descriptive** — `keywords[]`, `attribution`, `dimensions[]` (e.g. `TIME` / `ELEVATION`,
  with `default` and `values`), `metadataUrls[]`, `layerPath[]` (ancestor titles)
- **Service metadata** (duplicated onto every row) — `serviceTitle`, `serviceOrg`,
  `serviceContact`, `fees`, `accessConstraints`, `requestFormats` (GetMap / GetFeatureInfo
  MIME formats), and more
- **Envelope** — `_endpoint`, `_wmsVersion`, `_rowIndex`, and a lossless `_raw` of the
  layer's own definition

Every optional field is nullable and read by node/attribute presence, so a layer that
omits an element yields `null` rather than an error.

### Behaviour and resilience

- An endpoint that returns 4xx/5xx, times out, or serves a non-WMS body (an HTML error /
  anti-bot page, a `ServiceExceptionReport`, or malformed XML) is **skipped with a
  warning**; the batch continues.
- A run where **every** endpoint was skipped **fails fast** (non-zero exit) so nothing
  broken ships.
- A reachable WMS that advertises **0 named layers** yields 0 rows and a clean exit — the
  actor never fabricates rows.
- XML is parsed with external-entity resolution disabled (XXE-safe).

### Pricing

Pay-per-event: charged per extracted **layer** row.

# Actor input Schema

## `endpoint` (type: `string`):

A single OGC Web Map Service base URL or a full GetCapabilities URL. WMS is THE OGC standard for map/raster services — national mapping agencies, NOAA/USGS/NASA, EU INSPIRE and every national SDI, plus every GeoServer / MapServer / QGIS-Server / ArcGIS / MapProxy deployment. Paste the service root as given (e.g. https://ows.terrestris.de/osm/service); the SERVICE=WMS\&REQUEST=GetCapabilities\&VERSION=... parameters are appended for you if missing. Required unless you use the multi-endpoint list below.

## `endpoints` (type: `array`):

Optional: harvest the SAME layer catalog across several WMS services in one run. Each emitted row is tagged with its source endpoint (\_endpoint). Provide this OR the single endpoint above (at least one is required). An endpoint that is unreachable, behind an anti-bot wall, returns an error page or is not a WMS is skipped with a warning; the batch continues.

## `version` (type: `string`):

Which WMS version to request. 1.3.0 (default, current — namespaced, uses CRS + EX\_GeographicBoundingBox) or 1.1.1 (legacy — un-namespaced, uses SRS + LatLonBoundingBox). Both are fully parsed either way; the actual version is read from the served document and reported as \_wmsVersion. If the endpoint URL already pins a VERSION, that wins.

## `maxRecords` (type: `integer`):

A GLOBAL cap on the number of layer rows to emit across ALL endpoints (each row is one advertised layer and one billable event). Parsing stops as soon as the cap is reached, so a small value is a cheap deterministic sample. Leave empty to emit every advertised layer.

## `userAgent` (type: `string`):

Optional override for the HTTP User-Agent header sent with each request. Leave empty to send a descriptive default. Some servers gate a bare/anonymous User-Agent.

## `timeoutSecs` (type: `integer`):

Optional per-request read timeout in seconds (connect stays a fast 15s). Raise it for a very large capabilities document on a slow server. Leave empty for the default (60s).

## `bearer` (type: `string`):

Optional bearer token for an auth-gated WMS deployment (sent as Authorization: Bearer \*\*\*). NOT required for public services. Never logged.

## `extraHeaders` (type: `object`):

Optional extra HTTP headers as a JSON object, e.g. {"X-Api-Key": "..."} for a gateway-fronted instance. Not required for the public servers. Header values are never logged.

## Actor input object example

```json
{
  "endpoint": "https://ows.terrestris.de/osm/service",
  "version": "1.3.0",
  "maxRecords": 500
}
```

# Actor output Schema

## `results` (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 = {
    "endpoint": "https://ows.terrestris.de/osm/service",
    "maxRecords": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("datamule/wms-capabilities-extractor").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 = {
    "endpoint": "https://ows.terrestris.de/osm/service",
    "maxRecords": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("datamule/wms-capabilities-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "endpoint": "https://ows.terrestris.de/osm/service",
  "maxRecords": 500
}' |
apify call datamule/wms-capabilities-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=datamule/wms-capabilities-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "WMS Capabilities Extractor",
        "description": "Point at any OGC Web Map Service (WMS 1.1.1 or 1.3.0) and extract a structured layer catalog from its GetCapabilities document — one row per layer with CRS, bounding boxes, styles, scale, keywords, dimensions and service metadata. One actor, any WMS.",
        "version": "0.1",
        "x-build-id": "Ap1Bnm7zfG4Hpfccv"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/datamule~wms-capabilities-extractor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-datamule-wms-capabilities-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/datamule~wms-capabilities-extractor/runs": {
            "post": {
                "operationId": "runs-sync-datamule-wms-capabilities-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/datamule~wms-capabilities-extractor/run-sync": {
            "post": {
                "operationId": "run-sync-datamule-wms-capabilities-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "endpoint": {
                        "title": "WMS endpoint URL",
                        "type": "string",
                        "description": "A single OGC Web Map Service base URL or a full GetCapabilities URL. WMS is THE OGC standard for map/raster services — national mapping agencies, NOAA/USGS/NASA, EU INSPIRE and every national SDI, plus every GeoServer / MapServer / QGIS-Server / ArcGIS / MapProxy deployment. Paste the service root as given (e.g. https://ows.terrestris.de/osm/service); the SERVICE=WMS&REQUEST=GetCapabilities&VERSION=... parameters are appended for you if missing. Required unless you use the multi-endpoint list below."
                    },
                    "endpoints": {
                        "title": "WMS endpoint URL(s) — batch",
                        "type": "array",
                        "description": "Optional: harvest the SAME layer catalog across several WMS services in one run. Each emitted row is tagged with its source endpoint (_endpoint). Provide this OR the single endpoint above (at least one is required). An endpoint that is unreachable, behind an anti-bot wall, returns an error page or is not a WMS is skipped with a warning; the batch continues.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "version": {
                        "title": "WMS version",
                        "enum": [
                            "1.3.0",
                            "1.1.1"
                        ],
                        "type": "string",
                        "description": "Which WMS version to request. 1.3.0 (default, current — namespaced, uses CRS + EX_GeographicBoundingBox) or 1.1.1 (legacy — un-namespaced, uses SRS + LatLonBoundingBox). Both are fully parsed either way; the actual version is read from the served document and reported as _wmsVersion. If the endpoint URL already pins a VERSION, that wins.",
                        "default": "1.3.0"
                    },
                    "maxRecords": {
                        "title": "Max records (global cap)",
                        "minimum": 1,
                        "type": "integer",
                        "description": "A GLOBAL cap on the number of layer rows to emit across ALL endpoints (each row is one advertised layer and one billable event). Parsing stops as soon as the cap is reached, so a small value is a cheap deterministic sample. Leave empty to emit every advertised layer."
                    },
                    "userAgent": {
                        "title": "User-Agent override",
                        "type": "string",
                        "description": "Optional override for the HTTP User-Agent header sent with each request. Leave empty to send a descriptive default. Some servers gate a bare/anonymous User-Agent."
                    },
                    "timeoutSecs": {
                        "title": "Read timeout (seconds)",
                        "minimum": 1,
                        "maximum": 300,
                        "type": "integer",
                        "description": "Optional per-request read timeout in seconds (connect stays a fast 15s). Raise it for a very large capabilities document on a slow server. Leave empty for the default (60s)."
                    },
                    "bearer": {
                        "title": "Bearer token",
                        "type": "string",
                        "description": "Optional bearer token for an auth-gated WMS deployment (sent as Authorization: Bearer ***). NOT required for public services. Never logged."
                    },
                    "extraHeaders": {
                        "title": "Extra request headers",
                        "type": "object",
                        "description": "Optional extra HTTP headers as a JSON object, e.g. {\"X-Api-Key\": \"...\"} for a gateway-fronted instance. Not required for the public servers. Header values are never logged."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
