Forgot? Join

For region owners & developers

Region Monitoring API

A small, read-only HTTPS API that tells you how your region's simulator is doing right now: whether it is running, when the current simulator process started, and its frame rate, frame time, avatars, scripts and prims – measured on the server, not guessed from a viewer. It also gives your region's weather: now, the forecast, and ready-made words.

1. Get a key

  1. Log in on this website and open Control Panel → Regions, then your region, then Monitoring API keys (or go straight to Monitoring API keys).
  2. Give the key a name (for example the service that will use it) and choose All my regions or only the regions you pick.
  3. Press Create key and copy the key. It is shown only once: we store only a fingerprint (SHA-256) of it, so nobody – including us – can show it again. If you lose it, revoke it and make a new one.

Keys look like wtm_ followed by 48 letters and digits. Each owner can have up to 20 active keys. A key can read the regions you own or manage (estate managers listed on the region); that is checked again on every request, so if a region stops being yours the key stops seeing it.

Revoking a key

On the same page, press Revoke next to the key and confirm. It stops working at once, everywhere. Revoking one key does not affect your other keys. The list shows when each key was created and last used (updated at most once a minute).

This key is separate from the region's in-world console key (the Get API Key button), which is for the in-world control HUD.

2. Authentication

Send the key in the Authorization header as a bearer token, over HTTPS:

Authorization: Bearer wtm_your_key_here
  • Never put the key in the URL. A request with ?key=, ?api_key= or ?token= is refused with 400 key_in_url – revoke that key, because it may now be in logs.
  • HTTPS only. Use https://wolf-grid.com; plain-HTTP requests are redirected, which means your client has already sent the header unencrypted – so always start with https.
  • The key is never included in any response.

3. Endpoint & example

GET https://wolf-grid.com/api/v1/regions/{region_id}/status

{region_id} is the region's UUID. It never changes, even if the region is renamed. The Monitoring API keys page lists every region you own or manage with its id; it is also the id=… at the end of the region's page address in the Control Panel.

curl

curl -s https://wolf-grid.com/api/v1/regions/d87eb383-be72-11ec-8d47-005056455866/status \
  -H "Authorization: Bearer $WOLF_API_KEY"

Example answer (region online)

{
    "api_version": "1.0",
    "region_id": "d87eb383-be72-11ec-8d47-005056455866",
    "region_name": "Sandbox",
    "status": "online",
    "server_time_utc": "2026-10-23T18:00:02Z",
    "sample_time_utc": "2026-10-23T17:59:55Z",
    "sample_age_seconds": 7,
    "simulator": {
        "instance_id": "6a0f3b1c9e2d4f5a8b7c6d5e4f3a2b1c",
        "started_at_utc": "2026-10-23T09:14:31Z",
        "uptime_seconds": 31531,
        "fps": 55.0,
        "frame_time_ms": 18.19,
        "agent_count": 2,
        "physics_frame_time_ms": 0.42,
        "script_frame_time_ms": null,
        "active_scripts": 311,
        "script_events_per_second": 96.0,
        "prims": 8412,
        "prim_allowance": 45000,
        "pending_downloads": 0,
        "pending_uploads": 0,
        "time_dilation": 1.0,
        "physics_fps": 55.0,
        "npc_count": 0,
        "fps_actual": 11.0,
        "frame_time_ms_actual": 90.95,
        "stats_sample_time_utc": "2026-10-23T17:59:51Z",
        "stats_age_seconds": 11,
        "stats_available": true,
        "stats_unavailable_reason": null,
        "opensim_version": "OpenSimWolf 0.9.3.9333",
        "physics_engine": "BulletSim",
        "region_size_m": { "x": 1024, "y": 1024 }
    }
}

Values are illustrative.

4. Fields & units

A value that is not known is null, never 0. A 0 is a real measurement. All times are UTC in ISO 8601 (2026-10-23T18:00:02Z), to the second.

Top-level fields
FieldTypeUnitMeaning
api_versionstringVersion of this response format ("1.0").
region_idstringUUIDThe region's permanent id (stays the same when the name changes).
region_namestringThe region's current name.
statusstringonline, starting, restarting, stopping or offline - see Status values.
server_time_utcstringISO 8601 UTCWhen this answer was made.
sample_time_utcstringISO 8601 UTCWhen the region server last read its simulator processes (status, instance, start time).
sample_age_secondsintegersecondsserver_time_utc minus sample_time_utc. Normally under 15 + 5.

simulator

simulator fields
FieldTypeUnitMeaning
instance_idstring|null32 hexIdentifies the running simulator process. It changes every time the process starts - after a restart, a crash or a server reboot - and never otherwise. null when no simulator is running.
started_at_utcstring|nullISO 8601 UTCWhen the running simulator process started, read from the operating system's record of that process (see below).
uptime_secondsinteger|nullsecondsTime since started_at_utc.
fpsnumber|nullframes/s (viewer scale)Simulator frame rate as the viewer's Statistics bar shows it: 55 is full speed.
frame_time_msnumber|nullms (viewer scale)Average total frame time on the same scale (18.2 ms = 55 fps).
agent_countinteger|nullavatarsAvatars in the region now (root agents, not counting NPCs). A count only - never who.
physics_frame_time_msnumber|nullms (viewer scale)Time per frame spent in physics.
script_frame_time_msnullAlways null for now: the region's script engine does not measure script time per frame, so we do not report a made-up 0. Use script_events_per_second.
active_scriptsinteger|nullscriptsRunning scripts in the region.
script_events_per_secondnumber|nullevents/sScript events (timers, touches, listens, sensors...) delivered per second.
primsinteger|nullprimsObjects/prims in the region now.
prim_allowanceinteger|nullprimsThe region's configured prim capacity.
pending_downloadsinteger|nullrequestsAsset downloads waiting (queue).
pending_uploadsinteger|nullrequestsAsset uploads waiting (queue).
time_dilationnumber|null0-11.0 = simulation runs at real time.
physics_fpsnumber|nullframes/s (viewer scale)Physics frame rate.
npc_countinteger|nullNPCsScripted NPC avatars (not counted in agent_count).
fps_actualnumber|nullframes/sThe real number of frames per second (the viewer scale divided by the simulator's normalisation factor, 5 on this grid).
frame_time_ms_actualnumber|nullmsThe real average frame time (about 91 ms at full speed on this grid).
stats_sample_time_utcstring|nullISO 8601 UTCWhen the simulator measured the performance values above (it writes them every 10 s).
stats_age_secondsinteger|nullsecondsHow old those values are. Over 60 s and they are reported as null.
stats_availablebooleantrue when the performance values are present and fresh.
stats_unavailable_reasonstring|nullWhy they are not: simulator_not_running, simulator_update_pending (see below), no_sample_yet, sample_stale, region_stopping.
opensim_versionstring|nullSimulator software version.
physics_enginestring|nullPhysics engine in use (for example BulletSim or ubODE).
region_size_mobject|nullmetres{"x": width, "y": height}.

simulator_update_pending: the performance values (fps, frame times, agents, scripts, prims) come from a part of the simulator added on 23 October 2026. A region gets it the next time it restarts. Until then status, instance_id and started_at_utc work normally, and the performance values are null with this reason.

5. Status values

online
– the simulator is running and the region has finished starting (logins enabled).
starting
– the simulator process is running (or about to be launched) but the region has not finished starting.
restarting
– a restart is in progress: the region is shutting down or between processes and will come back by itself.
stopping
– the simulator is shutting down and no restart was asked for through the grid's controls.
offline
– no simulator is running for the region. You still get a full answer, with the simulator values null.

Watch instance_id to detect restarts and crashes: if it differs from your last poll, a new simulator process started at started_at_utc, even if you never saw the region offline.

6. Errors

Errors are JSON with a fixed shape, and never contain internal details:

{ "error": { "code": "invalid_api_key", "message": "Missing, unknown or revoked API key. ..." } }
Error responses
HTTPcodeWhen
400invalid_region_idThe id is not a UUID (status takes the UUID only).
400invalid_regionWeather: the region is neither a UUID nor a name of up to 64 characters.
400ambiguous_region_nameWeather: more than one of the regions this key may read has that name. Use its UUID.
400invalid_hours / invalid_kindWeather: hours is not 1-48, or kind is not now, stats, later or 1-48.
400key_in_urlA key was put in the query string. Revoke it.
400https_requiredNot called on https://wolf-grid.com.
401invalid_api_keyNo key, an unknown key, or a revoked key.
403insufficient_scopeThe key may not read region status.
404region_not_foundNo such region, or not one this key may read (both look the same, on purpose).
404not_foundWrong path.
404no_weatherWeather: the region shows no weather (its Wolf Environment, automatic environment and Weather tab are all off), or its place's forecast has not been read yet. The message says which; control is included.
404no_forecastWeather text: the forecast does not reach that many hours ahead (or the weather was set by hand and has none).
503weather_unavailableWeather: the weather service is not available just now.
405method_not_allowedAnything but GET (the API is read-only).
429rate_limitedToo many requests; wait the number of seconds in Retry-After.
500internal_errorA fault on our side. Retry later.
503monitoring_unavailableWe could not ask the region's server just now. This is not "offline": it says nothing about the region. The body still has region_id, region_name and "status": "unknown"; Retry-After says when to try again.

So: 200 with "status": "offline" means the simulator is not running; 503 means our monitoring could not tell.

7. Rate limits & caching

  • 60 requests per minute per key. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time the window resets).
  • 120 requests per minute per client address, and 10 failed authentications per minute per address.
  • Over a limit you get 429 rate_limited with Retry-After in seconds.
  • The region server is read at most every 15 seconds and the simulator writes its performance values every 10 seconds, so polling more often than every 15–30 seconds gives you the same data. Use sample_age_seconds and stats_age_seconds to see how fresh each part is.

8. Where the numbers come from

  • Start time and instance id come from the Linux kernel's own record of the running simulator process on the region server: the process's start time (/proc/<pid>/stat field 22, in clock ticks since boot, plus the boot time). The instance id is a hash of the server, its boot id, the process id and that start time, so it changes on every process start - including after a crash - and is the same on every poll while that process runs. Neither comes from a database, a viewer, this website or a previous API call. We chose this over the simulator's own clock because it works for every region today and cannot be reset by anything inside the simulator.
  • Status comes from the same live process list, plus the region's own "ready" signal (the same moment the simulator enables logins).
  • Performance values are the simulator's own statistics (the ones a viewer's Statistics bar shows), written by the simulator every 10 seconds on the region server.
  • The website reads them from each region server over a private connection. The simulator's own admin, console and REST interfaces are not used and not exposed.
  • Not provided in v1: last shutdown reason and backup status, because we cannot yet report them reliably.

Questions or ideas for v2: contact us.

9. Weather

The weather your region shows: the real weather of its place (from MET Norway), or the weather its owner chose in the Wolf Environment, or the weather set by hand. The same report a script gets with wolfCall("weather.now"). Same key, same rules: the key's owner must own or manage the region; the endpoints count towards the same 60 requests a minute.

GET https://wolf-grid.com/api/v1/regions/{region}/weather
GET https://wolf-grid.com/api/v1/regions/{region}/weather/forecast?hours=24      (1-48, default 24)
GET https://wolf-grid.com/api/v1/regions/{region}/weather/text?kind=now          (now, stats, later or 1-48 hours ahead)

{region} is the region's UUID or its name (URL-encoded, any case: Lone%20Wolf%20Test). Only the regions your key may read are matched; if two of them share a name you get 400 ambiguous_region_name; use the UUID.

  • How fresh: the real weather is MET Norway's forecast for the place, which our grid reads again when MET says it has a new one (at most every 10 minutes, usually about every half hour). now.time is the hour the reading is for and now.fetched_at when it was read; now.stale is true when it is over 6 hours old. Asking more often than every few minutes gives the same answer.
  • Not known is null, as everywhere in this API: weather chosen by the owner has no temperature, gusts and some chances are only given in some areas, and MET gives no visibility (always null).
  • Credit: MET Norway's data is licensed under CC BY 4.0 / NLOD 2.0 and must be credited. Every answer carries attribution.text ("Source: MET Norway (met.no)"); show it with the weather. Outside the Nordic countries the thunder risk is Open-Meteo.com's (CC BY 4.0), listed in attribution.extra.
  • Units are in the answer's units: °C (names ending _f: °F), m/s (wind_kmh: km/h), wind direction in degrees it comes from, hPa at sea level, % for humidity, cloud, fog and chances, mm in the hour, degrees above the horizon for the sun and moon. The place's local time follows its own time zone, summer time included.
  • control says what runs the weather: by wolf-environment, auto-environment, region-weather or none; source live (the real place), fixed or pattern (chosen by the owner, type / pattern), or manual (set by hand).
  • It is the region's weather; a parcel with its own weather is not included.

curl

curl -s https://wolf-grid.com/api/v1/regions/Tafelberg/weather \
  -H "Authorization: Bearer $WOLF_API_KEY"

curl -s "https://wolf-grid.com/api/v1/regions/c63d4ff5-91fc-47d0-ba3b-3cdbbc00388d/weather/forecast?hours=2" \
  -H "Authorization: Bearer $WOLF_API_KEY"

curl -s "https://wolf-grid.com/api/v1/regions/Tafelberg/weather/text?kind=stats" \
  -H "Authorization: Bearer $WOLF_API_KEY"

Example answer: /weather (Tafelberg, 26 October 2026)

{
    "api_version": "1.0",
    "region": {
        "id": "c63d4ff5-91fc-47d0-ba3b-3cdbbc00388d",
        "name": "Tafelberg"
    },
    "attribution": {
        "text": "Source: MET Norway (met.no)",
        "licence": "CC BY 4.0 / NLOD 2.0",
        "url": "https://api.met.no/doc/License",
        "extra": [
            "Thunder risk: Open-Meteo.com (CC BY 4.0)"
        ]
    },
    "generated_at_utc": "2026-10-26T08:04:25Z",
    "control": {
        "by": "wolf-environment",
        "mode": "wolf",
        "source": "live",
        "type": null,
        "pattern": null,
        "place_kind": "real"
    },
    "place": {
        "name": "South Africa",
        "short": "South Africa",
        "country": "",
        "kind": "real",
        "lat": -28.8166,
        "lon": 24.9916,
        "timezone": "Africa/Maseru",
        "utc_offset": 7200,
        "utc_offset_text": "UTC+2",
        "local_time": "2026-10-26T10:04:25+02:00",
        "local_time24": "10:04",
        "local_time12": "10:04 AM",
        "grid_time24": "01:04"
    },
    "now": {
        "time": "2026-10-26T07:00:00Z",
        "fetched_at": "2026-10-26T07:48:44Z",
        "stale": false,
        "condition": "Few clouds",
        "symbol": "fair_day",
        "weather_code": 801,
        "kind": "clear",
        "level": 0,
        "precip_type": "none",
        "precipitation_mm": 0.0,
        "precipitation_chance": null,
        "temperature_c": 24.9,
        "temperature_f": 76.8,
        "feels_like_c": 24.9,
        "feels_like_f": 76.8,
        "dew_point_c": 10.1,
        "humidity": 39,
        "pressure_hpa": 1013.8,
        "wind_ms": 10.1,
        "wind_kmh": 36.4,
        "wind_from_deg": 359,
        "wind_compass": "N",
        "wind_beaufort": 5,
        "wind_words": "fresh breeze",
        "gust_ms": null,
        "cloud": 13,
        "cloud_low": 0,
        "cloud_medium": 13,
        "cloud_high": 0,
        "fog": 0,
        "thunder_chance": 0,
        "uv_index": 5.6,
        "visibility": null,
        "aurora": 0,
        "synthetic": false
    },
    "sky": {
        "computed_at": "2026-10-26T08:04:25Z",
        "sun_elevation": 57.8,
        "sun_azimuth": 65.8,
        "is_day": true,
        "sunrise": "2026-10-26T03:30:44Z",
        "sunset": "2026-10-26T16:37:15Z",
        "sunrise_local": "05:30",
        "sunset_local": "18:37",
        "polar": null,
        "next_sun_event": {
            "kind": "sunset",
            "time": "2026-10-26T16:37:15Z",
            "local": "18:37",
            "tomorrow": false
        },
        "next_sun_event_local": "18:37",
        "moon_elevation": -60.6,
        "moon_azimuth": 254.4,
        "moon_illuminated": 100,
        "moon_waxing": false,
        "moon_phase": "full moon"
    },
    "text": {
        "now": "It's 25°C in South Africa with a few clouds and a fresh northerly wind. Sunset is at 18:37.",
        "stats": "🌍 Weather for South Africa:\nFew clouds\n🌡 24.90 °C (76.80 °F)\n💧 Humidity: 39%\n⏲ Pressure: 1014 hPa\n🌬 Wind: 10.10 m/s @ 359° (N)\n🌅 Sunrise 05:30, sunset 18:37\n🕒 Local time: 10:04 (24h) / 10:04 AM (AM/PM) [UTC+2]\nSource: MET Norway (met.no)\nThunder risk: Open-Meteo.com (CC BY 4.0)"
    },
    "units": {
        "temperature": "°C (names ending _f: °F)",
        "humidity": "%",
        "pressure": "hPa (at sea level)",
        "wind": "m/s (wind_kmh: km/h); direction = degrees the wind comes FROM, clockwise from north",
        "cloud": "% of the sky",
        "fog": "% of the area",
        "precipitation": "mm in the hour",
        "chance": "%",
        "elevation": "degrees above the horizon",
        "time": "ISO 8601 (UTC ends in Z); local* and *_local times are the place's own clock"
    }
}

Example answer: /weather/forecast?hours=2

{
    "api_version": "1.0",
    "region": {
        "id": "c63d4ff5-91fc-47d0-ba3b-3cdbbc00388d",
        "name": "Tafelberg"
    },
    "attribution": {
        "text": "Source: MET Norway (met.no)",
        "licence": "CC BY 4.0 / NLOD 2.0",
        "url": "https://api.met.no/doc/License",
        "extra": [
            "Thunder risk: Open-Meteo.com (CC BY 4.0)"
        ]
    },
    "generated_at_utc": "2026-10-26T08:04:25Z",
    "control": {
        "by": "wolf-environment",
        "mode": "wolf",
        "source": "live",
        "type": null,
        "pattern": null,
        "place_kind": "real"
    },
    "place": "{ ... as above ... }",
    "hours": 2,
    "forecast": [
        {
            "time": "2026-10-26T09:00:00Z",
            "unix": 1793005200,
            "local": "2026-10-26T11:00:00+02:00",
            "local24": "11:00",
            "hours_ahead": 0.93,
            "condition": "Clear sky",
            "symbol": "clearsky_day",
            "weather_code": 800,
            "precip_type": "none",
            "temperature_c": 30.2,
            "temperature_f": 86.4,
            "precipitation_mm": 0.0,
            "precipitation_chance": null,
            "cloud": 9,
            "fog": 0,
            "humidity": 28,
            "wind_ms": 12.0,
            "wind_from_deg": 345,
            "wind_compass": "NNW",
            "gust_ms": null,
            "thunder_chance": null,
            "text": "Later this morning (11:00): clear skies, 30°C, strong breeze from the north."
        },
        {
            "time": "2026-10-26T10:00:00Z",
            "unix": 1793008800,
            "local": "2026-10-26T12:00:00+02:00",
            "local24": "12:00",
            "hours_ahead": 1.93,
            "condition": "Few clouds",
            "symbol": "fair_day",
            "weather_code": 801,
            "precip_type": "none",
            "temperature_c": 31.7,
            "temperature_f": 89.1,
            "precipitation_mm": 0.0,
            "precipitation_chance": null,
            "cloud": 27,
            "fog": 0,
            "humidity": 24,
            "wind_ms": 11.9,
            "wind_from_deg": 326,
            "wind_compass": "NW",
            "gust_ms": null,
            "thunder_chance": null,
            "text": "Later this afternoon (12:00): a few clouds, 32°C, strong breeze from the north-west."
        }
    ],
    "units": "{ ... as above ... }"
}

MET's forecast is hourly for the first two days or so (further out its rows are 6 hours apart). Each row has its own ready-made text.

Example answer: /weather/text?kind=later

{
    "api_version": "1.0",
    "region": {
        "id": "c63d4ff5-91fc-47d0-ba3b-3cdbbc00388d",
        "name": "Tafelberg"
    },
    "attribution": {
        "text": "Source: MET Norway (met.no)",
        "licence": "CC BY 4.0 / NLOD 2.0",
        "url": "https://api.met.no/doc/License",
        "extra": [
            "Thunder risk: Open-Meteo.com (CC BY 4.0)"
        ]
    },
    "generated_at_utc": "2026-10-26T08:04:25Z",
    "kind": "later",
    "text": "Later this afternoon (16:00): a few clouds, 32°C, strong breeze from the west."
}

kind=now is one sentence ("It's 25°C in South Africa with a few clouds and a fresh northerly wind. Sunset is at 18:37."), kind=stats the multi-line block shown in text.stats above, kind=later about 6 hours ahead, or a number for that many hours ahead.