Rainlog API (1.0.0)

Download OpenAPI specification:

help@rainlog.org: help@rainlog.org

Rainlog API for accessing data collected on Rainlog.org

The read-only endpoints require no authentication.

The personal weather station upload endpoints are write-only, and authenticate with credentials Rainlog issues per gauge.

Last updated 2026-09-01.

Reading

A reading for a single day

Search daily readings

Filter readings and return a list. This will return Readings whose

  • readingId are in the list of readingIds
  • AND readingDate intersects the date range
  • AND position intersects the specified region
  • AND gaugeType matches the list of types specified
  • AND gaugeIds match the list of gaugeIds specified
  • AND userIds match the list of userIds specified
  • AND gaugeStatus matches the list of statuses specified
  • AND quality matches the list of qualities specified
  • AND timeWindowHour matches the list of hours specified Omitted or null fields will disable filtering on those fields. An explicitly empty gaugeIds list still means "match nothing". This endpoint returns BOTH hand-entered readings and the daily totals derived from automatic (personal weather station) gauges. Where a gauge has both for the same day and window, the hand-entered reading wins. Automatic rows report readingId 0, which is how you tell the two apart. Automation is a property of the DAY, not of the gauge: a gauge that switched between manual and automatic keeps each day under whichever mechanism was active then, so one gauge's history routinely contains both kinds of row. Results are ordered by readingDate ascending. Pagination is applied after filtering.
Request Body schema: application/json
required

Criteria by which to filter the data

readingIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
quality
Array of strings or null (ReadingQuality)
Enum: "Good" "Trace" "Poor" "Snow" "Lost" "Absent"

Filter by reading quality. Omitted or null disables the filter, which is equivalent to listing all six qualities.

timeWindowHour
Array of integers or null <int32> [ items <int32 > ]
Enum: 0 7

Filter by the hour the reading day ends: 7 for 7am-7am, 0 for midnight-to-midnight. Omitted or null disables the filter and returns BOTH windows, which for an automatic gauge means two rows per day. Pass [7] to get the classic 7am-7am series.

object

Describes how to limit the result size

gaugeRevisionIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
dateRangeStart
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

dateRangeEnd
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

object or object (GeoRegion)
gaugeType
Array of strings or null (GaugeType)
Enum: "TruChek" "SimpleCatch" "FunnelCatch" "FunnelCatchWithOverflow" "TippingBucketOrElectronic" "Other"

Filter by gauge type. Omitted or null disables the filter, which is equivalent to listing every type.

currentOnly
boolean
Default: false

When true, return only the newest revision of each gauge.

newestInDateRangeOnly
boolean
Default: false

When true, and a date range is supplied, return only the newest revision of each gauge that was already in effect at dateRangeStart, rather than every revision created in the range. Ignored when both dateRangeStart and dateRangeEnd are omitted.

gaugeIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
userIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
gaugeStatus
Array of strings or null (GaugeStatus)
Enum: "Active" "Inactive" "InactiveHidden"

Filter by gauge status. Omitted or null disables the filter and returns gauges of every status, including Inactive and InactiveHidden ones.

Responses

Response Schema: application/json
Array
readingId
integer <int64>

Identifier of the hand-entered reading. Readings derived from an automatic (personal weather station) gauge are not rows in the manual reading table and report 0 here.

gaugeId
integer <int64> (DatabaseId)
gaugeRevisionId
integer <int64> (DatabaseId)
remarks
string or null
readingDate
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

readingHour
integer <int32>

Hour the reading was taken, time local to gauge GPS coordinate.

readingMinute
integer <int32>

Minute the reading was taken, time local to gauge GPS coordinate.

quality
string (ReadingQuality)
Enum: "Good" "Trace" "Poor" "Snow" "Lost" "Absent"

Describes the quality and type of reading

rainAmount
number or null <double>

Liquid precipitation, in inches. The nullability of this field changes with quality.

  • "Good", "Trace", or "Poor"
    • This value must not be null
  • "Snow"
    • This value may be null
  • "Lost", or "Absent"
    • This value must be null
snowDepth
number or null <double>

Snow depth, in inches. The nullability of this field changes with quality.

  • "Good", "Trace", "Poor", "Lost" or "Absent"
    • This value must be null
  • "Snow"
    • This value may be null
snowAccumulation
number or null <double>

Snow accumulation, in inches. The nullability of this field changes with quality.

  • "Good", "Trace", "Poor", "Lost" or "Absent"
    • This value must be null
  • "Snow"
    • This value may be null
timeWindowHour
integer <int32>
Enum: 0 7

Hour at which the reading day ends, local to the gauge. 7 means the reading covers 7am-7am, 0 means midnight-to-midnight. These are the only two legal values. An automatic gauge produces a row for BOTH windows for the same day from the same observations, so if you do not filter on timeWindowHour you will get two rows per automatic gauge per day.

Request samples

Content type
application/json
{
  • "gaugeIds": [
    • 0
    ],
  • "userIds": [
    • 0
    ],
  • "gaugeStatus": [
    • "Active"
    ],
  • "pagination": {
    • "offset": 0,
    • "limit": 1000
    }
}

Response samples

Content type
application/json
[
  • {
    • "readingId": 0,
    • "gaugeId": 0,
    • "gaugeRevisionId": 0,
    • "remarks": "string",
    • "readingDate": "1999-02-25",
    • "readingHour": 7,
    • "readingMinute": 0,
    • "quality": "Good",
    • "rainAmount": 0,
    • "snowDepth": null,
    • "snowAccumulation": null,
    • "timeWindowHour": 7
    }
]

MonthReading

A reading for an entire month. There are very few of these readings and this type was never properly supported. It is not recommended for use in any data studies.

Search monthly readings

Filter month readings and return a list. This will return GaugeRevisions whose

  • monthReadingId are in the list of monthReadingIds
  • AND readingDate intersects the date range
  • AND position intersects the specified region
  • AND gaugeType matches the list of types specified
  • AND gaugeIds match the list of gaugeIds specified
  • AND userIds match the list of userIds specified

Omitted or null fields will disable filtering on those fields.

Request Body schema: application/json
required

Criteria by which to filter the data

monthReadingIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
object

Describes how to limit the result size

gaugeRevisionIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
dateRangeStart
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

dateRangeEnd
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

object or object (GeoRegion)
gaugeType
Array of strings or null (GaugeType)
Enum: "TruChek" "SimpleCatch" "FunnelCatch" "FunnelCatchWithOverflow" "TippingBucketOrElectronic" "Other"

Filter by gauge type. Omitted or null disables the filter, which is equivalent to listing every type.

currentOnly
boolean
Default: false

When true, return only the newest revision of each gauge.

newestInDateRangeOnly
boolean
Default: false

When true, and a date range is supplied, return only the newest revision of each gauge that was already in effect at dateRangeStart, rather than every revision created in the range. Ignored when both dateRangeStart and dateRangeEnd are omitted.

gaugeIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
userIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
gaugeStatus
Array of strings or null (GaugeStatus)
Enum: "Active" "Inactive" "InactiveHidden"

Filter by gauge status. Omitted or null disables the filter and returns gauges of every status, including Inactive and InactiveHidden ones.

Responses

Response Schema: application/json
Array
monthReadingId
integer <int64> (DatabaseId)
gaugeId
integer <int64> (DatabaseId)
gaugeRevisionId
integer <int64> (DatabaseId)
readingDate
string

The month this reading covers, as the first day of that month (2026-08-01 means August 2026). There are no separate year and month fields.

remarks
string or null
rainAmount
number or null <double>

Liquid precipitation for the whole month, in inches.

Request samples

Content type
application/json
{
  • "gaugeIds": [
    • 0
    ],
  • "userIds": [
    • 0
    ],
  • "gaugeStatus": [
    • "Active"
    ],
  • "pagination": {
    • "offset": 0,
    • "limit": 1000
    }
}

Response samples

Content type
application/json
[
  • {
    • "monthReadingId": 0,
    • "gaugeId": 0,
    • "gaugeRevisionId": 0,
    • "readingDate": "1999-02-25",
    • "remarks": "string",
    • "rainAmount": 0
    }
]

Gauge

A rain gauge. The actual properties of the gauge are stored in its GaugeRevision. A gauge may be modified over its life, but may not be moved more than a quarter mile from it's original location.

Search gauges

Filter gauges and return a list. A gauge is an identity that ties its revisions together; its properties live on the GaugeRevision.

Request Body schema: application/json
required

Filter Gauges and return a list. This will return Gauges whose

  • gaugeIds match the list of gaugeIds specified
  • AND userIds match the list of userIds specified
  • AND gaugeStatus matches the list of statuses specified

gaugeIds, userIds, and gaugeStatus may be null or omitted to disable filtering on those fields

gaugeIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
userIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
gaugeStatus
Array of strings or null (GaugeStatus)
Enum: "Active" "Inactive" "InactiveHidden"

Filter by gauge status. Omitted or null disables the filter and returns gauges of every status, including Inactive and InactiveHidden ones.

object (Pagination)

Describes how to limit the result size

Responses

Response Schema: application/json
Array
gaugeId
integer <int64> (DatabaseId)
userId
integer <int64> (DatabaseId)
name
string
status
string (GaugeStatus)
Enum: "Active" "Inactive" "InactiveHidden"

User specified activity state of gauge

sortOrder
integer <int32>

Display order of this gauge within its owner's gauge list.

pwsWuKey
string or null

The gauge's Weather Underground upload key, used as PASSWORD when posting observations to /AutomaticObservation/createFromWu. This is a credential, so it is returned ONLY to the gauge's owner or to an admin. It is null for an unauthenticated caller and for any other user, including on gauges that do have a key. Manual gauges have no key and report null for everyone.

Request samples

Content type
application/json
{
  • "gaugeIds": [
    • 0
    ],
  • "userIds": [
    • 0
    ],
  • "gaugeStatus": [
    • "Active"
    ],
  • "pagination": {
    • "offset": 0,
    • "limit": 1000
    }
}

Response samples

Content type
application/json
[
  • {
    • "gaugeId": 0,
    • "userId": 0,
    • "name": "Bob's gauge",
    • "status": "Active",
    • "sortOrder": 0,
    • "pwsWuKey": null
    }
]

GaugeRevision

Type and location of a gauge. Revisions exist because a gauge can be updated by a user, but it should not retroactively affect old readings.

Search gauge revisions

Filter gauge revisions and return a list. A revision is the type and location of a gauge at a point in time, so this is where a reading's position, gauge type and timezone come from.

Request Body schema: application/json
required

Filter gauge revisions and return a list. This will return GaugeRevisions whose

  • gaugeRevisionId matches the list of gaugeRevisionIds
  • AND existence intersects the date range
  • AND position intersects the specified region
  • AND gaugeType matches the list of types specified
  • AND gaugeIds match the list of gaugeIds specified
  • AND userIds match the list of userIds specified

gaugeRevisionIds, region, gaugeType, gaugeIds, and userIds may be null or omitted to disable filtering on those fields

gaugeRevisionIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
dateRangeStart
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

dateRangeEnd
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

object or object (GeoRegion)
gaugeType
Array of strings or null (GaugeType)
Enum: "TruChek" "SimpleCatch" "FunnelCatch" "FunnelCatchWithOverflow" "TippingBucketOrElectronic" "Other"

Filter by gauge type. Omitted or null disables the filter, which is equivalent to listing every type.

currentOnly
boolean
Default: false

When true, return only the newest revision of each gauge.

newestInDateRangeOnly
boolean
Default: false

When true, and a date range is supplied, return only the newest revision of each gauge that was already in effect at dateRangeStart, rather than every revision created in the range. Ignored when both dateRangeStart and dateRangeEnd are omitted.

object

Describes how to limit the result size

gaugeIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
userIds
Array of integers <int64> (DatabaseId) [ items <int64 > ]
gaugeStatus
Array of strings or null (GaugeStatus)
Enum: "Active" "Inactive" "InactiveHidden"

Filter by gauge status. Omitted or null disables the filter and returns gauges of every status, including Inactive and InactiveHidden ones.

Responses

Response Schema: application/json
Array
gaugeRevisionId
integer <int64> (DatabaseId)
gaugeId
integer <int64> (DatabaseId)
createdDate
string (ISO8601Date)

Short ISO 8601 formatted date, formatted YYYY-MM-DD

brand
string or null
model
string or null
description
string or null
object (GpsCoordinate)
gaugeType
string (GaugeType)
Enum: "TruChek" "SimpleCatch" "FunnelCatch" "FunnelCatchWithOverflow" "TippingBucketOrElectronic" "Other"

Currently allowed types of gauges

gaugeTypeOther
string or null
reportAutomationMode
string
Enum: "manual" "automatic"

How this revision reports. "manual" means a person enters readings; "automatic" means the gauge is a personal weather station whose daily totals are machine-derived from its uploads. The mode that applies to any given day is the one on the revision that was active THAT DAY, not the newest revision.

timeWindowHour
integer or null <int32>
Enum: 0 7 null

Hour at which this revision's reading day ends: 7 for 7am-7am, 0 for midnight-to-midnight. Null when reportAutomationMode is "automatic", because an automatic gauge generates both windows rather than picking one.

timezone
string or null

IANA timezone identifier for gauge location

Request samples

Content type
application/json
{
  • "gaugeIds": [
    • 0
    ],
  • "userIds": [
    • 0
    ],
  • "gaugeStatus": [
    • "Active"
    ],
  • "pagination": {
    • "offset": 0,
    • "limit": 1000
    }
}

Response samples

Content type
application/json
[
  • {
    • "gaugeRevisionId": 0,
    • "gaugeId": 0,
    • "createdDate": "1999-02-25",
    • "brand": "AccuGauge",
    • "model": "AccuGauge 2000",
    • "description": "On South side of house",
    • "position": {
      },
    • "gaugeType": "TruChek",
    • "gaugeTypeOther": null,
    • "reportAutomationMode": "manual",
    • "timeWindowHour": 7,
    • "timezone": "America/Phoenix"
    }
]

Data Export

Bulk download of Rainlog's data as zipped CSVs. Everything Rainlog holds is public, so these need no authentication: the global export is the whole dataset, and the per user export is the same data narrowed to one account. Station credentials are redacted from the raw upload payloads either way.

Download the whole public dataset

The nightly export of every gauge, reading and observation in Rainlog, as a zip of CSVs. Regenerated once a day, so the same URL gives that day's artifact to everyone. The rollover is just after midnight America/Phoenix; before that instant the request resolves to the previous day's artifact, because the current day's has not been written yet.

With includePwsPayloads=true you get the superset artifact instead: the same CSVs plus automatic_observation_payloads.csv, the raw per-upload payload for every accepted PWS upload, exactly as the station submitted it. Station credentials are redacted in that file: PASSWORD, PASSKEY and mac, plus ID / stationid, which are redundant with its own gauge_id column. This is the same redaction PwsPayload/getLatest applies to its raw format.

query Parameters
includePwsPayloads
boolean
Default: false

Include the raw per-upload PWS payload CSV. Adds roughly 150 MB uncompressed, so ask for it only if you want the submitted values rather than Rainlog's parsed readings.

Responses

Response Schema: application/zip
string <binary>

Response samples

Content type
application/json
{
  • "code": "ProtocolError",
  • "message": "string"
}

Download one user's data

The same export as DataDump/globalData, narrowed to one user's gauges, readings, monthly readings, observations and notification subscriptions. Built on request rather than nightly, so it reflects the data as of the call.

includePwsPayloads=true adds that user's raw upload payloads, one CSV per gauge, under the same redaction as the global artifact.

query Parameters
userId
required
integer <int64> (DatabaseId)

The user whose data to export.

includePwsPayloads
boolean
Default: false

Include this user's raw per-upload PWS payload CSVs.

Responses

Response Schema: application/zip
string <binary>

Response samples

Content type
application/json
{
  • "code": "ProtocolError",
  • "message": "string"
}

PWS Upload

Observation upload for automatic gauges (personal weather stations). These are the endpoints a station console, a bridge, or your own uploader posts to; the Rainlog apps themselves never use them. Everything below applies to both protocols; each operation then documents only what is specific to it.

Credentials are per gauge, and are not your Rainlog account login. Rainlog issues every automatic gauge its own: a station id and key for the Weather Underground protocol, a path token for Ecowitt. Those are the only things the station ever sends, your Rainlog username and password will not authenticate an upload, and a station may upload only to the gauge its credentials were issued for. Find them in Rainlog under Data > View/Edit Gauges, then the key icon on the gauge, which opens a window called Automated Upload Credentials. The gauge's Report Automation Mode must be set to Automatic first (its newest revision must have reportAutomationMode automatic): a manual gauge has no credentials at all, and an upload for one is rejected. Station-side setup, with screenshots: https://rainlog.org/help/pws-setup

Both protocols accept GET and POST. The documented method for each is the one real stations use: WU consoles send a GET with query parameters, Ecowitt consoles send a form encoded POST. Either endpoint accepts the other form with the same field names, so a hand-rolled uploader can pick whichever is easier.

How often to upload. There is a ceiling and a floor, and they are set by different things.

The ceiling is the rate limit: one accepted observation per gauge per 295 seconds, about one every five minutes, on both protocols. Going faster is not an error, just wasted: the extra upload is rejected for rate, and still audited. Nothing is gained by trying, since the daily rollup works in hourly buckets.

The floor is what the daily rollup needs to see. It works in hourly buckets, and a bucket counts as covered when it holds at least one observation, so roughly one upload per hour is the practical minimum. A day produces a daily reading only if all three hold: at least 12 covered hours, at least 50% of that day's hours covered, and no gap longer than 12 consecutive hours. Miss any of them and the day generates nothing at all, which is why a station that only reports when it rains can go missing from the map entirely.

Coverage also decides quality. A day with every one of its hours covered is Good; anything less that still clears the gate is Poor, which counts for less in the interpolated map.

Target one upload every five minutes, with a minimum of one per hour. Hourly is the floor, not a goal, and it leaves no margin at all, because one upload that fails to send loses that hour outright. Enough lost hours and the day drops from Good to Poor, or out of the rollup entirely. At five-minute intervals a given hour has a dozen chances to be covered, so an outage has to last a full hour before it costs anything. Upload on that cadence whether or not it is raining: a dry hour still has to be covered to count.

What happens to an upload. Only the rain fields become observation data. Any other field a station sends is accepted rather than rejected, but has no effect on rainfall totals. Nothing is thrown away: every accepted upload is kept verbatim, exactly as the station submitted it, with only the credential redacted, so the original reading remains available even for fields Rainlog does not currently interpret.

A rain value above 30 inches is still recorded, but the observation is flagged Poor quality so it does not poison daily totals or the interpolated map. Negative values and -9999 style "no reading" sentinels are discarded. Observations more than 2 hours in the future or more than 7 days in the past are rejected outright, and the gauge owner is notified.

Read a gauge's latest upload

The newest automatic observation for one automatic gauge.

Every other read endpoint here searches daily or monthly aggregates. This one answers what a gauge is reading now, which is what an integration needs and what nothing else returned.

Rain and snow come from Rainlog's own columns. Every other sensor is read out of the upload payload exactly as the station submitted it, which Rainlog keeps permanently and already publishes in the global data export.

query Parameters
gaugeId
required
integer <int64> (DatabaseId)

The gauge to read. Must be an automatic gauge.

format
string
Default: "raw"
Enum: "raw" "json" "wu" "ecowitt"

The shape to answer in.

raw (the default) returns the stored payload verbatim, redacted exactly as the global data export redacts it. json returns the named fields plus that same payload. wu and ecowitt return the payload untouched when the observation arrived on that protocol, and otherwise render it under the other protocol's parameter names.

A rendered record carries only the fields Rainlog models, so it can lose things raw would keep. The piezo rain family exists only in the Ecowitt protocol, so rendering as wu drops it. A piezo gauge still reports its headline rain as rainin (Rainlog folds the piezo hourly value into the observation's rain when there is no tipping bucket value), but its piezo daily, weekly, monthly, yearly, event and rate counters have no WU names and are lost. Ask for ecowitt or raw to keep them.

at
string

Read the newest upload at or before this time instead of the newest overall. An ISO-8601 instant (2026-09-01T12:00:00Z) or the yyyy-MM-dd HH:mm:ss UTC form the upload endpoints accept.

Asking for a point in time is deliberate, so the staleness rule below does not apply when at is given.

includeStale
boolean
Default: false

Return the newest upload even when it is too old to be a current reading. See the 404 below.

Responses

Response Schema:
string

Response samples

Content type
No sample

Read the latest upload for several gauges

The batch form of the GET. A gauge with no current reading is absent from the response rather than being an error, which covers a gauge that never reported and one that has gone stale.

In a text format the body is one record per line, each beginning with its ID=Rainlog<gaugeId> so records can be matched to gauges without relying on their order.

Request Body schema: application/json
required
gaugeIds
required
Array of integers <int64> (DatabaseId) [ items <int64 > ]

The gauges to read. Each must be an automatic gauge.

format
string
Default: "raw"
Enum: "raw" "json" "wu" "ecowitt"

See the format parameter on the GET form.

at
string

See the at parameter on the GET form.

includeStale
boolean
Default: false

See the includeStale parameter on the GET form.

Responses

Response Schema:
string

Request samples

Content type
application/json
{
  • "gaugeIds": [
    • 0
    ],
  • "format": "raw",
  • "at": "string",
  • "includeStale": false
}

Response samples

Content type
No sample

Upload an observation using the Weather Underground protocol

Accepts a Weather Underground style observation for one automatic gauge.

Also reachable at https://rainlog.org/weatherstation/updateweatherstation.php, which is the literal path WU-compatible firmware uses, so a station already configured for Weather Underground can be pointed at Rainlog by changing only the host name.

The gauge's credentials travel as ordinary request fields: ID is the station id Rainlog<gaugeId> (for example Rainlog4821) and PASSWORD is that gauge's station key. See PWS Upload above for where to find them.

Rate limit: one accepted observation per station per 295 seconds by default, so about one upload every five minutes.

The rain value stored for the observation is rainin, the WU rolling one hour total. hourlyrainin is accepted for uploaders that send the Ecowitt spelling, but it feeds only the separate hourly figure: it is not a fallback for the observation's rain, so an upload carrying hourlyrainin and no rainin records no rain. Send rainin.

Only precipitation becomes a Rainlog reading. The other sensors below are accepted and recorded with the upload, and the whole payload is retained verbatim, but they do not change the reading. Parameters not listed here are not rejected either: they are kept in the stored payload as sent.

How daily totals are derived, and which fields to send: Rainlog builds a reading day by differencing a cumulative counter between consecutive uploads, not by adding up the hourly values. Differencing is immune to upload phase, missed uploads and a varying upload rate, because the change between two samples is exactly the rain that fell in between.

The counter is chosen per gauge per reading day, in this order of preference:

  1. totalrainin, the all time counter. Best: it only resets on a factory reset.
  2. yearlyrainin, which resets on 1 January local time.
  3. dailyrainin, which resets at the station's local midnight. Last resort: every reset is a point where a little rain can be lost, and this one resets nightly.

(An Ecowitt upload can also supply a piezo year counter, which ranks between yearlyrainin and dailyrainin. There is no WU parameter for it.)

Where several are present, the one with the most readings in the day wins, ties going to the order above. A counter going backwards (a reset) or jumping by more than 30 inches contributes nothing and simply rebaselines.

If no counter at all is sent, Rainlog falls back to adding the rolling hour values, which is materially less accurate. Send totalrainin if your station has it, and send the counters even when they look redundant next to the hourly figure.

query Parameters
ID
required
string
Example: ID=Rainlog4821

Station id, in the form Rainlog<gaugeId>. Case insensitive on the Rainlog prefix. station, si, and stationId are accepted as aliases for Windy-compatible uploaders.

PASSWORD
required
string

The gauge's station key, issued by Rainlog per gauge (not your Rainlog account password). Compared case sensitively.

dateutc
string
Example: dateutc=2026-08-17 21:05:00

Time of the observation, as UTC wall clock: YYYY-MM-DD HH:MM:SS, YYYY-MM-DD HH:MM, RFC3339, or the literal now. If absent or unparsable the server falls back to time, then ts, then its own clock.

time
string

Fallback observation time when dateutc is absent. RFC3339, or now.

ts
integer <int64>

Fallback observation time when dateutc and time are absent. Unix epoch seconds.

rainin
number <double>

Rain in the past hour, in inches. The WU rolling one hour total, and the rain value stored for the observation.

rainratein
number <double>

Instantaneous rain rate, inches per hour.

eventrainin
number <double>

Rain so far in the current event, in inches.

hourlyrainin
number <double>

Ecowitt-flavored rolling one hour total, in inches. Used only when rainin is absent.

dailyrainin
number <double>

Rain so far today, in inches. Resets at the station's local midnight. Used as a last-resort daily counter for stations that emit nothing better.

weeklyrainin
number <double>

Rain so far this week, in inches.

monthlyrainin
number <double>

Rain so far this month, in inches.

yearlyrainin
number <double>

Rain so far this year, in inches. A monotonic counter, and the preferred source for daily totals after totalrainin.

totalrainin
number <double>

All time rain counter, in inches. The most preferred source for deriving daily totals, because it only ever increases.

tempf
number <double>

Outdoor temperature, degrees Fahrenheit.

humidity
number <double>

Outdoor relative humidity, percent.

dewptf
number <double>

Dew point, degrees Fahrenheit.

baromin
number <double>

Barometric pressure at sea level, inches of mercury.

windspeedmph
number <double>

Wind speed, miles per hour.

windgustmph
number <double>

Wind gust, miles per hour.

maxdailygust
number <double>

Highest gust so far today, miles per hour.

winddir
integer

Wind direction, degrees clockwise from north (0 to 359).

solarradiation
number <double>

Solar irradiance, watts per square metre.

UV
number <double>

UV index. uv is accepted as well, since firmwares disagree on the case.

indoortempf
number <double>

Indoor temperature, degrees Fahrenheit.

indoorhumidity
number <double>

Indoor relative humidity, percent.

softwaretype
string

Free text naming the uploading software. Recorded with the upload.

action
string

WU protocol mechanic, always updateraw in practice. Accepted and recorded with the upload; it does not affect the reading.

realtime
string

WU protocol mechanic marking a realtime feed, 1 in practice. Accepted and recorded with the upload.

rtfreq
string

WU protocol mechanic giving the realtime interval in seconds. Accepted and recorded with the upload. It does not change the rate limit, which is fixed per gauge.

Responses

Response Schema: text/plain
string
Enum: "success" "INVALIDREQUEST" "INVALIDPASSWORDID" "RATELIMIT" "FUTURE_TIMESTAMP" "BACKFILL_TOO_OLD"

Upload an observation using the Ecowitt protocol

Accepts an Ecowitt style observation for one automatic gauge.

Also reachable at https://rainlog.org/data/report/{token}, which is the literal path Ecowitt consoles use for a custom server, so a station can be pointed at Rainlog through its normal "custom weather server" settings.

Authentication has two parts. The path token is the gauge's credential, issued by Rainlog (see PWS Upload above for where to find it). PASSKEY is the station's own identifier rather than anything Rainlog issues: the first successful upload for a token binds whatever PASSKEY it presents to that gauge, and every later upload must present the same one. Regenerating the token clears the binding.

Rate limit: one accepted observation per gauge per 295 seconds by default, so about one upload every five minutes.

Only precipitation becomes a Rainlog reading. The other sensors below are accepted and recorded with the upload, and the whole payload is retained verbatim, but they do not change the reading. Parameters not listed here are not rejected either: they are kept in the stored payload as sent, which is where a console's extra channels, battery levels and air quality fields end up.

How daily totals are derived, and which fields to send: Rainlog builds a reading day by differencing a cumulative counter between consecutive uploads, not by adding up the hourly values. Differencing is immune to upload phase, missed uploads and a varying upload rate, because the change between two samples is exactly the rain that fell in between.

The counter is chosen per gauge per reading day, in this order of preference:

  1. totalrainin, the all time counter. Best: it only resets on a factory reset.
  2. yearlyrainin, which resets on 1 January local time.
  3. yrain_piezo, the piezo year counter, used when the tipping bucket fields are absent.
  4. dailyrainin, which resets at the station's local midnight. Last resort: every reset is a point where a little rain can be lost, and this one resets nightly.

Where several are present, the one with the most readings in the day wins, ties going to the order above. A counter going backwards (a reset) or jumping by more than 30 inches contributes nothing and simply rebaselines.

If no counter at all is sent, Rainlog falls back to adding the rolling hour values, which is materially less accurate. Send totalrainin if your station has it, and send the counters even when they look redundant next to the hourly figure.

Both rain families are understood. The stored hourly rain value is the first of hourlyrainin, hrain_piezo, eventrainin, erain_piezo that is present, so a console reporting both a tipping bucket and a piezo gauge prefers the tipping bucket.

path Parameters
token
required
string

The gauge's Ecowitt path token, issued by Rainlog per gauge (not your Rainlog account password). Compared case sensitively.

Request Body schema: application/x-www-form-urlencoded
required

The observation fields, form encoded. This is what an Ecowitt console sends. The same fields are also accepted as query parameters on a GET, which is useful for a hand-rolled uploader or a quick curl test.

PASSKEY
required
string

The station's own passkey, as emitted by the console. Bound to the gauge on first use and required to match thereafter.

dateutc
string

Time of the observation, as UTC wall clock: YYYY-MM-DD HH:MM:SS, YYYY-MM-DD HH:MM, RFC3339, or the literal now. If absent or unparsable the server uses its own clock.

rainratein
number <double>

Tipping bucket rain rate, inches per hour.

eventrainin
number <double>

Tipping bucket rain so far in the current event, in inches.

hourlyrainin
number <double>

Tipping bucket rain in the past hour, in inches. First choice for the stored rain value.

dailyrainin
number <double>

Tipping bucket rain so far today, in inches. Resets at the station's local midnight.

weeklyrainin
number <double>

Tipping bucket rain so far this week, in inches.

monthlyrainin
number <double>

Tipping bucket rain so far this month, in inches.

yearlyrainin
number <double>

Tipping bucket rain so far this year, in inches. A monotonic counter that resets on 1 January local time, and the second choice for deriving daily totals after totalrainin.

totalrainin
number <double>

Tipping bucket all time rain counter, in inches. The most preferred source for deriving daily totals, because it only ever increases. Send it if the station has it.

rrain_piezo
number <double>

Piezo (WS90/WS85) rain rate, inches per hour.

erain_piezo
number <double>

Piezo rain so far in the current event, in inches.

hrain_piezo
number <double>

Piezo rain in the past hour, in inches. Used for the stored rain value when hourlyrainin is absent.

drain_piezo
number <double>

Piezo rain so far today, in inches. Resets at the station's local midnight.

wrain_piezo
number <double>

Piezo rain so far this week, in inches.

mrain_piezo
number <double>

Piezo rain so far this month, in inches.

yrain_piezo
number <double>

Piezo rain so far this year, in inches. Third choice for deriving daily totals, used when the tipping bucket counters are absent.

srain_piezo
integer
Enum: 0 1

Piezo rain-start indicator, 1 or 0. Not an accumulation.

tempf
number <double>

Outdoor temperature, degrees Fahrenheit.

humidity
number <double>

Outdoor relative humidity, percent.

dewptf
number <double>

Dew point, degrees Fahrenheit.

baromrelin
number <double>

Barometric pressure at sea level, inches of mercury.

baromabsin
number <double>

Absolute (station) pressure, inches of mercury.

windspeedmph
number <double>

Wind speed, miles per hour.

windgustmph
number <double>

Wind gust, miles per hour.

maxdailygust
number <double>

Highest gust so far today, miles per hour.

winddir
integer

Wind direction, degrees clockwise from north (0 to 359).

solarradiation
number <double>

Solar irradiance, watts per square metre.

uv
number <double>

UV index.

tempinf
number <double>

Indoor temperature, degrees Fahrenheit. This is the Ecowitt spelling of the WU protocol indoortempf.

humidityin
number <double>

Indoor relative humidity, percent. This is the Ecowitt spelling of the WU protocol indoorhumidity.

stationtype
string

Console firmware string, for example GW1100B_V2.4.5. Recorded with the upload.

model
string

Console model, for example GW1100B. Recorded with the upload.

freq
string

Sensor radio frequency, for example 915M. Recorded with the upload.

runtime
integer <int64>

Console uptime in seconds. Recorded with the upload.

heap
integer <int64>

Console free heap in bytes. Recorded with the upload.

Responses

Response Schema: text/plain
string
Enum: "success" "INVALIDREQUEST" "INVALIDTOKEN" "INVALIDPASSKEY" "RATELIMIT" "FUTURE_TIMESTAMP" "BACKFILL_TOO_OLD"