TREM intensity reports
trem.intensity.v1 is the seismic-intensity report for ExpTech TREM-net (the TREM seismic network). While the ground is shaking, it lists townships at each intensity level from what the stations actually observed. One episode of shaking produces a series of reports (the first report, updates, and a final report). Each report is the complete picture at that moment. The format is XML.
How to connect, decode, and reconnect is in SSE connection.
TREM data notice
TREM data are automated, real-time observations from ExpTech's own station network (TREM-net). They are not official earthquake information. In Taiwan, the authority on earthquake information is the Central Weather Administration (CWA) of the Ministry of Transportation and Communications, and information published by the CWA always prevails. In this notice, TREM data means TREM-net observations and detections. TREM EEW is provided only to specific users who have ExpTech's individual approval or a contract, and its use is governed by Article 4-1 of the Terms of Service.
When you use TREM data, you must:
- Defer to the CWA.Earthquake reports, earthquake early warnings, seismic intensity and tsunami information published by the CWA prevail. Where TREM data differ from official information, follow the official information.
- Label it as unofficial.Wherever you present TREM data, show the data source together with the statement "Unofficial information; the Central Weather Administration's releases prevail."
- Comply with the Meteorological Act.Under Article 17(1) of Taiwan's Meteorological Act, forecasts and warnings of earthquakes are issued exclusively by the central meteorological authority, and Article 18 does not allow other agencies, schools, organizations or individuals to issue earthquake forecasts or any warnings to the general public; providing them to specific users is not restricted. Violations are subject to a fine of NT$200,000 to NT$1,000,000 under Article 24(2), which may be imposed for each violation.
- Never use it unlawfully.Do not use TREM data to predict earthquakes or to issue earthquake or tsunami warnings, and do not spread unverified earthquake information. Spreading rumours or false information about disasters that harms the public or others is punishable under Article 53(3) of the Disaster Prevention and Protection Act by up to three years of imprisonment, detention, or a fine of up to NT$1,000,000. Spreading rumours that may disturb public peace is punishable under Article 63(1)(5) of the Social Order Maintenance Act by detention of up to three days or a fine of up to NT$30,000.
- Know its limits.TREM observations are automated and not reviewed by staff. Noise, device faults or network delays can cause false detections, missed events or delays. Never use them as the sole basis for evacuation, closures or any other safety decision.
Data license
Section titled “Data license”AttributionData: ExpTech Studio TREM (exptech.dev) · Non-commercial use · Unofficial information; the Central Weather Administration's releases prevail
Where to show it, examples and commercial-use requests: Data licensing and attribution.
Authentication
Section titled “Authentication”No token is required (public topic). If you send a valid token, usage is recorded under the permission trem.intensity, and going over the quota does not disconnect the stream. See SSE connection: Authentication.
Subscribe
Section titled “Subscribe”- Multiplexed entry:
/api/v1/trem/sse?topics=trem.intensity.v1, event nametrem.intensity.v1. - Single-topic path:
tokenAPI Token (optional). Prefer the Authorization: Bearer header.
curl -N --compressed "https://api.lb.exptech.dev/api/v1/trem/sse/intensity"curl -N --compressed "https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/intensity"curl -N --compressed "https://api.lb-khh1.exptech.dev/api/v1/trem/sse/intensity"const res = await fetch("https://api.lb.exptech.dev/api/v1/trem/sse/intensity");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}const res = await fetch("https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/intensity");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}const res = await fetch("https://api.lb-khh1.exptech.dev/api/v1/trem/sse/intensity");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}import requests
with requests.get( "https://api.lb.exptech.dev/api/v1/trem/sse/intensity", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)import requests
with requests.get( "https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/intensity", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)import requests
with requests.get( "https://api.lb-khh1.exptech.dev/api/v1/trem/sse/intensity", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)text/event-stream; data is base64(gzip(XML))
retry: 3000
event: infodata: {"location":"lb-tpe1"}
data: H4sIAAAAAAAC/...The token is invalid, expired, or revoked
invalid tokenmode has no effect on this topic. Every report is sent.
Message format
Section titled “Message format”Each decoded data is one UTF-8 XML document (no <?xml ?> declaration):
<Report> <Control> <Title>震度速報</Title> <PublishingOffice>ExpTech Studio</PublishingOffice> <PublishingRegion>core-tnn1</PublishingRegion> </Control> <Head> <ReportDateTime>2026-07-26T20:36:41.869+08:00</ReportDateTime> <EventID>20260726203631</EventID> <InfoType>發表</InfoType> <Serial>1</Serial> <RelatedEewEvents> <EventID>20260726203631</EventID> <EventID>20260726203639</EventID> </RelatedEewEvents> </Head> <Body> <Intensity> <Observation> <MaxInt>2</MaxInt> <Area level="2"> <Town code="100"></Town> <Town code="105"></Town> <Town code="220"></Town> </Area> <Area level="1"> <Town code="239"></Town> <Town code="260"></Town> <Town code="330"></Town> </Area> </Observation> </Intensity> </Body></Report>| Field | Type | Description |
|---|---|---|
Control/Title | string | Always 震度速報. |
Control/PublishingOffice | string | Always ExpTech Studio. |
Control/PublishingRegion | string | Name of the node that published this report, for example core-tnn1. It changes when the node switches. |
Head/ReportDateTime | string | Publish time of this report, ISO 8601 in Taiwan time (+08:00), down to milliseconds. |
Head/EventID | string | Identifier of this episode of shaking, format YYYYMMDDHHMMSS (Taiwan time). It is the detection time of the first earthquake event that started this shaking. Every report for the same episode uses the same value. |
Head/InfoType | string | 發表 (the first report), 更新 (update), or 最終 (the last report for this shaking). The format also reserves 取消 (canceled). |
Head/Serial | int | Which report this is for the episode, starting at 1 and increasing. |
Head/RelatedEewEvents/EventID | string | Identifier of an earthquake event that caused this shaking. Same format as Head/EventID. There may be more than one, and the list usually includes Head/EventID itself. When there are none, the whole RelatedEewEvents element is omitted. |
Body/Intensity/Observation/MaxInt | string | Maximum seismic intensity in this report. Symbols are in the table below. 0 when no township reached intensity 1. |
Observation/Area | element | Townships that share one intensity level, ordered from higher intensity to lower. Only intensity 1 and above are listed. |
@level | string | Intensity level of this group. Symbols are in the table below. |
Town@code | int | Township code (a 3-digit postal code, for example 100 is Zhongzheng District, Taipei City), sorted ascending within the group. |
Intensity symbols
Section titled “Intensity symbols”MaxInt and Area@level use these symbols:
| Symbol | 0 |
1 |
2 |
3 |
4 |
5- |
5+ |
6- |
6+ |
7 |
|---|---|---|---|---|---|---|---|---|---|---|
| Intensity | 0 | 1 | 2 | 3 | 4 | 5 lower | 5 upper | 6 lower | 6 upper | 7 |
Township codes
Section titled “Township codes”Town@code is a 3-digit township code (the same as the postal code). For the county or city, the township name, and the coordinates of a code, use ExpTech’s public region.json.
Advanced example
Section titled “Advanced example”A browser receives the single-topic path: onmessage reads the data, decodes it, parses the XML with DOMParser, and deduplicates with EventID + Serial.
async function decodeData(b64) { const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip")); return await new Response(stream).text();}
const latestSerial = new Map(); // EventID -> latest Serial already handled
const es = new EventSource("https://api.lb.exptech.dev/api/v1/trem/sse/intensity");es.onmessage = async (e) => { const doc = new DOMParser().parseFromString(await decodeData(e.data), "application/xml"); const eventId = doc.querySelector("Head > EventID").textContent; const serial = Number(doc.querySelector("Head > Serial").textContent); if (serial <= (latestSerial.get(eventId) ?? 0)) return; // replay or duplicate latestSerial.set(eventId, serial);
const infoType = doc.querySelector("Head > InfoType").textContent; const maxInt = doc.querySelector("MaxInt").textContent; console.log(`intensity report ${eventId} #${serial} (${infoType}) max intensity ${maxInt}`); for (const area of doc.querySelectorAll("Observation > Area")) { const towns = [...area.querySelectorAll("Town")].map((t) => Number(t.getAttribute("code"))); console.log(` intensity ${area.getAttribute("level")}: ${towns.join(", ")}`); }};- A new connection replays recent reports. As soon as the connection is up, you first receive the latest report of each intensity episode whose
EventIDis within the last 240 seconds, and live data follows. A reconnect after a drop can repeat a report you already handled. Deduplicate withEventID+Serial, and process only aSerialnewer than the one you know. - A later report replaces an earlier one. Each report is the complete picture at that moment, not a diff. For one episode of shaking, the report with the largest
Serialis the current one. - One episode of shaking can match several earthquake events.
RelatedEewEventscan contain more than oneEventID. Do not assume there is only one. - Only townships at intensity 1 or higher are listed. A township that appears in no
Areahad no observed intensity of 1 or higher in this report. - Time.
ReportDateTimeandEventIDare both Taiwan time (UTC+8).
