Application Programming Interface
The ground motion database is equipped with a web interface, which is what you are currently viewing,
and an application programming interface (API) that exposes the data to HTTP requests. The web
interface is useful if you quickly want to look at data in a tabular form, but it is cumbersome if
you want to develop an end-to-end workflow. Copying HTML tables is kind of a pain, and it takes time
to render those tables. The API exposes the underlying data through HTTP requests, providing
the data in JSON format so you can work with it more directly and incoporate it into a workflow.
Users can access the API by authenticating, retrieving an authorization token, and submitting a request
that includes that token in the request header. The authorization token is required because we want to make sure
that we return the information only to people who have an active account, thereby avoiding attacks by
unauthorized users, and that users have access to data consistent with their role (user, modeler, admin).
Quick Start
The script below shows the whole workflow: it logs in, queries a table, picks five records whose time series can be downloaded from the flatfile, and downloads their acceleration time series. The sections that follow explain each step.
import getpass
import requests
base = 'https://nga-gm.org'
username = input('username: ')
r = requests.get(base + '/users/login', headers={'Accept': 'application/json'},
auth=(username, getpass.getpass('password: ')))
headers = {'Accept': 'application/json', 'Authorization': 'Bearer ' + r.json()['token']}
# A table: 10 events.
events = requests.get(base + '/events?limit=10', headers=headers).json()
# 5 records of magnitude over 6 whose time series can be downloaded, from the flatfile.
params = {'select': 'motion_id,magnitude,rrup', 'where': 'public_time_series=1 AND magnitude>6', 'limit': 5}
motion_ids = requests.get(base + '/flatfile', headers=headers, params=params).json()['motion_id']
# Their time series (acceleration in g).
data = {'checked_motion_ids': motion_ids, 'format': 'json', 'component': 'acc_h1,acc_h2'}
for ts in requests.post(base + '/unscaled-motions', headers=headers, json=data).json()['output']:
acc_h1 = ts['acc_h1']
dt = 1 / ts['sampling_rate']
Authentication / Authorization
The first step in using the API is to authenticate and retrieve an authorization token. To authenticate,
submit an API request to the following uniform resource locator (URL)
https://www.nga-gm.org/users/login
You must use the Basic Auth method to include your username and password with the HTTP request.
Programs specifically designed for making HTTP requests include curl,
postman, and others. In this documentation, we provide
examples using the Python requests package.
The example below authenticates the user using HTTP Basic Auth and retrieves an authorization token.
We suggest never hard-coding your username and password into a script. You might accidentally share
the script, and divulge your authentication credentials. We have used the getpass
package here facilitate password entry. The token is valid for 120 minutes, after which you will need to
re-authenticate and retrieve a new token. It is important not to share your token with others.
import requests
from requests.auth import HTTPBasicAuth
import getpass
import json
username = input('username: ')
password = getpass.getpass('password: ')
url = 'https://nga-gm.org/users/login'
headers = {"User-Agent":"XY", "Accept":"application/json"}
r = requests.get(url, headers=headers, auth=HTTPBasicAuth(username,password))
token = json.loads(r.text)['token']
Submitting a GET or POST Request
After retrieving an authorization token, you can now submit a GET or POST request to the desired
URL, and the requested data will be returned in JSON format. Two headers must be
included in your request. The "Accept":"application/json" header is used to identify that
JSON data is being requested. The "Authorization":"Bearer" header contains
the authorization token that was provided after successful authentication.
The API allows users to submit either GET or POST requests. A GET request is well-suited to short
query strings parameters that can easily fit at the end of a URL. Sometimes users might want
to include a long list of fields in their request (e.g., to retrieve all of the time_series_metadata
entries corresponding to a long list of motion_id fields). In that case, the query string for a
GET request would be very long, and might be truncated by the server, so a POST request is better.
In a POST request, the data is transmitted as a JSON string in the body of the request rather than
being appended to the URL. When users enter query string parameters into the URL to retrieve data
through the nga-gm.org website, they are using a GET request. POST requests require use of a
software package such as CURL, Javascript, Postman, or the Python requests package. The examples
below use the Python requests package.
Example GET Request
An example GET request is provided in the Python script below. In this case, 50 entries from the events table are requested. The URL structure and available query string parameters for customizing the request are discussed in the next section.
import pandas as pd
import requests
import json
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
url = 'https://nga-gm.org/events?limit=50'
r= requests.get(url, headers=headers)
df = pd.DataFrame.from_dict(json.loads(r.text))
Example POST Request
The same request can be made using the POST method as illustrated by the Python script below. This is a simple query that is better suited to a GET request than a POST request, but the example is nevertheless useful to demonstrate how post requests are made. Note that the URL no longer contains any query string parameters. Those are passed as a Python dictionary called "data" into the "json" field of the post request. A more complicated example requiring the POST method is also provided in the example queries section.
import pandas as pd
import requests
import json
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
url = 'https://nga-gm.org/events'
data = {'limit': 50}
r= requests.post(url, headers=headers, json=data)
df = pd.DataFrame.from_dict(json.loads(r.text))
URL structure
Requests are submitted to a uniform resource locator (URL) that contains the following components:
url = base_url/endpoint?query_string
base_url
The base_url is the webpage containing the API. In this case, it ishttps://www.nga-gm.orgSubmitting a request without also specifying an endpoint will produce an error. The endpoint is necessary so we know which query to run to retrieve the data you have requested. The query string is optional, and can be used to customize your queries.
endpoint
An endpoint is a location where an API receives requests about a resource. The endpoints correspond to table names converted to pluralized lower camel case with underscores removed. All of the tables except for the user table are accessible. The user table is not accessible for privacy purposes. Furthermore, a flatfile endpoint is also available. The flatfile endpoint combines tables together in a manner similar to the NGA flatfiles. Tables included in the flatfile endpoint are indicated with a checkmark in the table below. The flatfile can be generated for any pseudospectral acceleration component, or the effective amplitude spectra (i.e., smoothed Fourier spectra), but not more than one in a single query because the resulting table would be large and unwieldy. The psa_rotd50 table is returned in the flatfile endpoint by default.| table name | endpoint | table included in flatfile |
|---|---|---|
| aftershock_mainshock | aftershockMainshocks | |
| basin_model | basinModels | |
| basin_site | basinsSites | |
| citation | citations | |
| collections | collections | |
| collection_motion | collectionsMotions | |
| eas | eas | (✔) |
| event | events | ✔ |
| event_eqid | eventEqids | ✔ |
| event_geometry | eventsGeometries | |
| event_type | eventTypes | ✔ |
| finite_fault | finiteFaults | ✔ |
| finite_fault_kinematic_parameter | finiteFaultKinematicParameters | ✔ |
| finite_fault_segment | finiteFaultSegments | |
| intensity_measure | intensityMeasures | ✔ |
| motion | motions | ✔ |
| network | networks | ✔ |
| path | paths | ✔ |
| psa_h1 | psaH1 | (✔) |
| psa_h2 | psaH2 | (✔) |
| psa_rotd0 | psaRotd0 | (✔) |
| psa_rotd50 | psaRotd50 | (✔) |
| psa_rotd100 | psaRotd100 | (✔) |
| psa_v | psaV | (✔) |
| site | sites | ✔ |
| site_geometry | geometriesSites | |
| station | stations | ✔ |
| station_ssn | stationSsns | ✔ |
| time_series_data | timeSeriesData | |
| time_series_metadata | timeSeriesMetadata | ✔ |
| version | versions | |
| version_time_series_metadata | timeSeriesMetadataVersions | ✔ |
| vs30_citation | vs30Citations | |
| vs30_code | vs30Codes | |
| z_code | zCodes |
Endpoint names can also be written in lower case with hyphens, such as time-series-metadata for timeSeriesMetadata. Besides the table endpoints, these endpoints have sections of their own:
- flatfile: tables joined as in the NGA flatfiles.
- timeSeriesData: time series, by time_series_metadata_id.
- unscaledMotions: time series, by motion_id, as JSON or as AT2, VT2, DT2, and AVD files.
- selectionScaling (coming soon): selects and scales a suite of records for a target spectrum.
- search: finds records and fields by name, other name, or meaning.
query_string
A query string is a list of parameters used to customize the request. If a query string is not specified, default values will be returned. A list of valid query string parameters and their default values is below.query string options for table endpoints
| query string parameter | default | description |
|---|---|---|
| limit | 20 | number of records per page |
| sort | primary_key | field to sort by |
| direction | asc | sort direction (asc or desc) |
| page | 1 | page number |
| role | user | Role for users with elevated permissions (i.e., modeler or admin). |
| where | - | Used to filter selected data (e.g., ?where=pga_rotd50>0.1+AND+pga_rotd50<0.2 |
| select | all fields | comma separated list of fields to query |
| join | - | Comma-separated list of related tables to include (e.g., ?join=Stations,Sites). Only available on some endpoints. See "Joining Related Tables" below. |
First query string parameter
The first query string parameter comes after a question mark (?) at the end of the url. For example: https://www.nga-gm.org/intensityMeasures?page=1.Second and subsequent query string parameters
The 2nd and subsequent query string parameters are separated by an ampersand (&). For example: https://www.nga-gm.org/?page=1&limit=50.Filtering data using "where" statements
Data can be filtered using the "where" query string parameter. The where statement is used to create a SQL statement excerpt "WHERE [field] [operator] [value]". For example, to retrieve events with hypocenter latitude larger than 40.0°, the following should query string parameter would be ?where=hypocenter_latitude>40.0 This query string parameter will be translated to the SQL excerpt WHERE hypocenter_latitude > 40.0 when the query is executed. Available operators depend on data type as described in the table below.| type | operators |
|---|---|
| numeric (e.g., float, int) | >, >=, =, !=, <=, <, IN, NOT IN, IS NULL, IS NOT NULL |
| string | >, >=, =, !=, <=, <, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL |
| boolean | true, false |
Applying Multiple Conditions
Multiple operators can be applied using an "AND" or "OR" separator. When constructing the query string, spaces should be replaced by the plus sign "+", as shown below. For example,"?where=hypocenter_latitude<40+OR+hypocenter_longitude>-118.0"Furthermore, conditions can be grouped using parenthesis like this:
"?where=(hypocenter_latitude>40.0+AND+hypocenter_latitude<42.0)+OR+(hypocenter_longitude>-118.0+AND+hypocenter_longitude<-117.0)"
Equal sign (=) for float fields
You should avoid using the equal sign (=) operator for float-valued fields because floats are stored in an approximate manner. For example, if a strike appears as 196.2 in the database, you should recognize that its value may not be precisely 196.2 because we format outputs for presentation purposes. Therefore, performing a query "WHERE strike=196.2" is unlikely to return the entry you're looking for. You could use a combination of greater than (>) and less than (<) operators to search for a narrow range instead. It is fine to use the equal sign (=) operator for integer, string, and decimal fields. Decimal fields are used, for example, for latitude and longitude data such that all of the fields contain 5 decimals in the database, and can be compared using the equal operator. The schema page lists the type for each field. Strings are "varchar", integers are "int(N)" where N is the bit depth, floats are "float(N)", and decimal format is denoted "float(M,L)", where M is the total number of characters, and L is the number after the decimal point.LIKE and NOT LIKE
The LIKE operator is used to search for a specified pattern in a string.The NOT LIKE operator returns the complementary set of entries excluded by the LIKE operator.
Two wildcards are often used with LIKE and NOT LIKE statements:
- The asterisk (*) is a placeholder for an arbitrary number of characters
- The underscore (_) is a placeholder for a single character
| Query String | SQL Statement Excerpt | Description |
|---|---|---|
| where=event_name+LIKE+v* | WHERE event_name LIKE 'v%' | Finds any value that starts with "v" |
| where=event_name+NOT+LIKE+v* | WHERE event_name NOT LIKE 'v%' | Finds any value that does not start with "v" |
| where=event_name+LIKE+*v | WHERE event_name LIKE '%v' | Finds any value that ends with "v" |
| where=event_name+LIKE+*v* | WHERE event_name LIKE '%v%' | Finds any value that contains "v" in any position |
| where=event_name+LIKE+_v | WHERE event_name LIKE '_v' | Finds any value that contains "v" in the 2nd position |
| where=event_name+LIKE+v_* | WHERE event_name LIKE 'v_%' | Finds any value that starts with "v" and is at least 2 characters in length |
| where=event_name+LIKE+v__* | WHERE event_name LIKE 'v__%' | Finds any value that starts with "v" and is at least 3 characters in length |
| where=event_name+LIKE+v*n | WHERE event_name LIKE 'v%n' | Finds any value that starts with "v" and ends with "n" |
IN and NOT IN Operators
The IN operator is used to select values from an array of options, and is shorthand for multiple OR conditions.The NOT IN operator returns the complementary set of entries excluded by the IN operator.
Since the IN and NOT IN operators imply an equal sign (=), you should not use them for float fields for the reasons explained above.
Consider the examples below in which we search on an integer field called "event_id".
Note that the value array is a comma-separated list contained within parenthesis.
| Query String | SQL Statement Excerpt | Description |
|---|---|---|
| where=event_id+IN+(1,2,3) | WHERE event_id IN (1, 2, 3) | Finds entries with id = 1, 2, or 3 |
| where=event_id+NOT+IN+(1,2,3) | WHERE event_id NOT IN (1, 2, 3) | Finds entries with id ≠ 1, 2, or 3 |
IS NULL and IS NOT NULL Operators
The IS NULL operator finds entries with no value in a field, and IS NOT NULL finds entries with one. Neither takes a value. Some missing values are stored as -999 rather than left empty, so a search for missing values may need both, as in the second example. On the flatfile endpoint, a primary key that IS NULL finds time series with no record in that table, such as psa_h1_id IS NULL for time series with no psa_h1 record.
| Query string | SQL statement | Meaning |
|---|---|---|
| where=pga_h1+IS+NULL | WHERE pga_h1 IS NULL | Finds entries with no pga_h1 value |
| where=(pga_h1+IS+NULL+OR+pga_h1+=+-999) | WHERE (pga_h1 IS NULL OR pga_h1 = -999) | Finds entries missing pga_h1, empty or -999 |
| where=pga_h1+IS+NOT+NULL | WHERE pga_h1 IS NOT NULL | Finds entries with a pga_h1 value |
Flatfile Endpoint
The database is organized into many different tables to ensure data integrity, but users often want to query data from multiple tables and join them together in a way that results in repetition of certain fields. For example, users might want to retrieve all event, site, and ground motion data together in the same table, where event and site information is repeated for each ground motion. The flatfile endpoint provides the logic to join together tables that we believe will serve the needs of most users, and that match the published flatfiles.
The tables listed below are queried and presented in a "flattened" form via the flatfile endpoint. If users do not specify a select, sort, or where key that identifies the ground motion component, the Rotd50 component is used by default. If users specify a select field, the selected fields are returned along with the primary keys for all tables involved in the query. Other components can be queried instead by adding a "component" query string parameter to explicitly specify the component (e.g., "?component=PsaRotd100"), or by simply including a separate query string parameter that uses data from the table you wish to query (e.g., "?sort=psa_rotd100_0p010). In the latter case, the table(s) will be inferred from the provided query strings. Note that you can request data from multiple psa tables and the eas table. However, you should recognize that doing so may result in an amount of data that is too large to be provided by the server, or may result in slow response times. A time series with no record in a requested psa or eas table, or in the intensity_measure table, is still returned, with those fields left blank.
Like the other endpoints, the flatfile endpoint accepts a POST request, with the query string parameters (where, select, sort, direction, limit, page, and component) sent as JSON in the body of the request. Use it when the where clause is too long for a URL, such as a long list of motion_id values; servers commonly refuse URLs longer than about 8,000 characters. Request each page by sending its page number in the body. For example:
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
url = 'https://nga-gm.org/flatfile'
motion_ids = ','.join(str(m) for m in motion_id_list)
data = {'where': 'motion_id IN ({})'.format(motion_ids), 'component': 'rotd50', 'limit': 1000, 'page': 1}
r = requests.post(url, headers=headers, json=data)
df = pd.DataFrame.from_dict(json.loads(r.text))
On the nga-gm.org website, a where clause submitted by POST is saved, and the browser is sent to a URL like /flatfile?query=0123456789abcdef0123456789abcdef that refers to it, so the results can be paged through and sorted. A saved where clause is kept for a week. A where parameter given with query must also hold, which narrows the saved results further.
Published flatfiles
The published NGA-West3 flatfiles, as released, can be downloaded too: zipped CSV files, one for each component, with the station and source metadata and the documentation. They're listed under CSV Flatfiles on the flatfile page, and as JSON at /flatfile/files, by release: each file's name, size in bytes, and url. Download a file from its url with the same token:
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
releases = json.loads(requests.get('https://nga-gm.org/flatfile/files', headers=headers).text)['releases']
for file in releases['20250919']:
r = requests.get('https://nga-gm.org/' + file['url'], headers=headers, stream=True)
with open(file['name'], 'wb') as f:
for chunk in r.iter_content(chunk_size=1 << 20):
f.write(chunk)
Tables included in flatfile endpoint query
| table |
|---|
| motion |
| time_series_metadata |
| intensity_measure |
| psa_h1 |
| psa_h2 |
| psa_rotd0 |
| psa_rotd50 |
| psa_rotd100 |
| psa_v |
| eas |
| event |
| event_type |
| finite_fault |
| finite_fault_kinematic_parameter |
| station |
| site |
| station |
| network |
| path |
| event_eqid |
| station_ssn |
Time Series Data
The timeSeriesData endpoint returns acceleration time series by time_series_metadata_id, from GET or POST requests. Its parameters are:
- time_series_metadata_id: the time series to return, comma-separated. Without it, the time series are returned a page at a time.
- component: acc_h1, acc_h2, and acc_v, comma-separated (all three by default).
- limit (default 10) and page (default 1), when time_series_metadata_id isn't given.
Each item of the response is one time series: time_series_metadata_id, sampling_rate (samples per second), azimuth_h1 and azimuth_h2 (degrees), and the components asked for, as lists of accelerations in g. Only public time series are available. To request time series by motion_id instead, as the Selection and Scaling endpoint returns them, use the unscaledMotions endpoint (see Time Series Downloads).
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
url = 'https://nga-gm.org/timeSeriesData?time_series_metadata_id=15278,15279&component=acc_h1'
time_series = json.loads(requests.get(url, headers=headers).text)
Time Series Downloads
The unscaled-motions endpoint sends the time series of the motions listed in checked_motion_ids, in one of two formats, chosen with format in the body of a POST request:
- json: the time series themselves, as the time_series_data endpoint sends them. Each item of output is one time series, with its motion_id, time_series_metadata_id, sampling_rate (samples per second), azimuth_h1, azimuth_h2, and the components asked for with component (acc_h1, acc_h2, and acc_v, comma-separated; all three by default). errorMessage lists motions that have no time series in the database.
- zip (the default): a zip archive of AT2, VT2, DT2, and AVD files (chosen with at2, vt2, dt2, and avd), with the motions' metadata and response spectra as CSV files, as the Unscaled Motions page downloads. The response's file_url is where to fetch the zip, with the same token. Each zip can be fetched once, and is deleted after a day if it isn't. A zip download counts toward the monthly limit of 500 different motions; motions downloaded already this month don't count again.
Only motions with public time series can be downloaded (admins and modelers can also download others). The examples below download the acceleration time series of two motions as JSON, and then their AT2 files as a zip.
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
url = 'https://nga-gm.org/unscaled-motions'
data = {'checked_motion_ids': [21536, 21537], 'format': 'json', 'component': 'acc_h1,acc_h2'}
r = requests.post(url, headers=headers, json=data)
time_series = json.loads(r.text)['output']
data = {'checked_motion_ids': [21536, 21537], 'format': 'zip', 'at2': True}
r = requests.post(url, headers=headers, json=data)
file_url = json.loads(r.text)['file_url']
z = requests.get('https://nga-gm.org/' + file_url, headers=headers)
open('time_series.zip', 'wb').write(z.content)
Selection and Scaling (coming soon)
The selection-scaling endpoint runs the Ground Motion Selection and Scaling tool: it selects a suite of records whose scaled spectra match a target spectrum's mean and standard deviation, and returns the records with their scale factors. It searches all of NGA-West3, in its stable release. Send the criteria as JSON in a POST request, with the field names of the tool's form. A field left out gets the form's default; null leaves a range's minimum or maximum open.
- Nrecords (default 11, at most 100) and Nevent, the most records from one event (default 2).
- Ranges: Mmin and Mmax (magnitude, default 6 to 8), RrupMin and RrupMax (km, default 0 to 100), Vs30min and Vs30max (m/s, default 200 to 800), scaleMin and scaleMax (default 0.2 to 5), D595min and D595max and D575min and D575max (Arias durations in s of non-pulse records; D595min defaults to 5).
- Pulses: constrain_pulses (true or false), Npulse (the number of pulse records), and TpMin and TpMax (s), which apply only when constrain_pulses is true.
- component: the spectra's component, RotD50 (the default), RotD100, RotD0, H1, H2, or V (vertical). The default target spectrum is a horizontal one, so V needs a vertical target_spectrum. Records have no pulse periods for V, so the pulse fields don't apply to it.
- public_only (default true): search only records whose time series can be downloaded. If too few records meet the criteria, widen the ranges, or set it to false to search every record.
- target_spectrum: a list of {"period": s, "sa": g, "sd": standard deviation of ln PSA}, with at least three periods from 0.01 to 20 s. Without it, the tool's default spectrum is used.
- include_spectra (default false): also return the periods, the target, the suite's mean and standard deviation, and each record's scaled spectrum.
The response's records each have motion_id, scale (the scale factor), and the record's event, station, magnitude, rrup, vs30, Tp, and durations; summary says how many records met the criteria. Invalid fields return status 400 with a message for each in errors; criteria too narrow to fill the suite return 422; an invalid token returns 401. A user can run one selection at a time (429 otherwise). The time series aren't included: request them from the unscaled-motions endpoint (see Time Series Downloads), and multiply each by its record's scale factor.
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
criteria = {'Nrecords': 11, 'Mmin': 6.0, 'Mmax': 8.0, 'RrupMax': 100}
r = requests.post('https://nga-gm.org/selection-scaling', headers=headers, json=criteria)
records = json.loads(r.text)['records']
scale = {rec['motion_id']: rec['scale'] for rec in records}
data = {'checked_motion_ids': list(scale), 'format': 'json'}
r = requests.post('https://nga-gm.org/unscaled-motions', headers=headers, json=data)
for ts in json.loads(r.text)['output']:
acc_h1 = [a * scale[ts['motion_id']] for a in ts['acc_h1']]
Search
The search endpoint finds events, stations, networks, collections, citations, codes, and database fields by their names, by other names they are known by (Izmit for the Kocaeli earthquake, CSMIP for network CE), or by meaning ("focal depth" finds hypocenter_depth). It is the search behind the search box at the top of each page. Send a GET request with these query string parameters:
- q: what to search for (at most 200 characters).
- type (optional): result types, separated by commas, from event, station, field, network, collection, code, and citation.
- limit (optional): the most results, from 1 to 100 (default 20).
Each result has id (its type and key, such as event:127 or field:site.vs30), type, key (the record's primary key, or table.column for a field), title, subtitle, reason (why it matched), and url (the table page that shows it). Codes also have table (vs30_code, z_code, event_type, or basin_model). Events and stations also have actions: flatfile, the flatfile page filtered to them, and motions, the unscaled-motions page listing their motions. limited is true when the full search was unavailable and only names containing the query's words were searched.
A query that asks for the flatfile ("flatfile for the El Centro earthquake") or for ground motions ("download ground motions for Northridge", "Kobe records", "AT2 files for Landers") has intent flatfile or motions, and is searched without the words that ask for it: searched is what was searched for (El Centro earthquake, Northridge). Otherwise intent is null and searched is the query.
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
r = requests.get('https://nga-gm.org/search', headers=headers, params={'q': 'Kaikoura', 'type': 'event'})
for result in json.loads(r.text)['results']:
print(result['key'], result['title'], result['reason'])
Limits and Errors
Limits:
- A token is valid for 120 minutes. After that, log in again for a new one.
- Zip downloads from the unscaledMotions endpoint count toward a monthly limit of 500 different motions; motions downloaded already this month don't count again. Time series requested as JSON (from the unscaledMotions or timeSeriesData endpoint) don't count toward it.
- A zip can be fetched once from its file_url, and is deleted after a day if it isn't.
- A user can run one selection at a time with the selectionScaling endpoint, and a selection is stopped after 120 seconds. Typical selections take a few seconds.
- Very large requests can be slow or fail; see Requesting huge amounts of data.
Errors: the table, flatfile, and timeSeriesData endpoints answer a request they can't fulfil, such as one with an invalid token or an unknown field in a where filter, with status 200 and the error message as the response, a JSON string instead of the usual list. Check for that before using the data, for example with isinstance(response, str) in Python. The unscaledMotions and selectionScaling endpoints answer with an HTTP status code and a JSON object whose success is false and whose errorMessage says what's wrong:
| status | meaning |
|---|---|
| 400 | selectionScaling: invalid or unknown fields; errors has a message for each. |
| 401 | The token is missing, invalid, or expired. |
| 403 | unscaledMotions: some motions have no public time series. selectionScaling: the endpoint is limited to admins and modelers for now. |
| 404 | unscaledMotions: the zip was fetched already, deleted, or is another user's. |
| 405 | selectionScaling: the request wasn't a POST. |
| 422 | selectionScaling: too few records meet the criteria to fill the suite. |
| 429 | selectionScaling: a selection of yours is still running. |
| 500 | A problem on the server. Please let us know. |
Other unscaledMotions requests that can't be fulfilled, such as one with an invalid field or over the monthly limit, return status 200 with success false. Check success as well as the status.
Joining Related Tables
Some endpoints let you pull in related data from other tables without switching to the heavier Flatfile endpoint, using the "join" query string parameter. For example, "?join=Stations" on the events endpoint returns each event's own fields plus the station data for its associated ground motion. The joined table's fields appear directly alongside the endpoint's own fields, rather than as a nested object. Table names aren't case-sensitive and can be written with or without underscores/hyphens - "TimeSeriesMetadata", "timeSeriesMetadata", "time_series_metadata", and "time-series-metadata" all work the same.
Only specific, pre-approved combinations of tables can be joined together this way, since arbitrary joins could be expensive to compute or wouldn't make sense as a public query. The table below lists which tables can currently be joined to each endpoint that supports it.
| endpoint | tables that can be joined |
|---|---|
| events | Motions, Stations, Sites, Networks, EventTypes, FiniteFaults, Citations, FiniteFaultKinematicParameters, TimeSeriesMetadata, IntensityMeasures, Eas, PsaH1, PsaH2, PsaRotd0, PsaRotd50, PsaRotd100, PsaV |
| motions | Events, EventTypes, Stations, Sites, Networks, Paths, FiniteFaults, TimeSeriesMetadata, IntensityMeasures, Eas, PsaH1, PsaH2, PsaRotd0, PsaRotd50, PsaRotd100, PsaV |
| stations | Networks, Sites, Motions, Events, TimeSeriesMetadata, IntensityMeasures, Eas, PsaH1, PsaH2, PsaRotd0, PsaRotd50, PsaRotd100, PsaV |
| basin-models | Citations |
| basins-sites | Sites, BasinModels |
| collections | Citations |
| collections-motions | Collections, Motions |
| eas | TimeSeriesMetadata, Motions, Events, Citations |
| event-eqids | Events, Collections |
| events-geometries | Events, Geometries |
| finite-fault-kinematic-parameters | FiniteFaults |
| finite-fault-segments | FiniteFaults |
| finite-faults | Events, Citations |
| geometries | Citations |
| geometries-sites | Sites, Geometries |
| intensity-measures | TimeSeriesMetadata, Motions, Events, Citations |
| networks | Citations |
| paths | FiniteFaults, Stations |
| psa-h1 | TimeSeriesMetadata, Motions, Events, Citations |
| psa-h2 | TimeSeriesMetadata, Motions, Events, Citations |
| psa-rotd0 | TimeSeriesMetadata, Motions, Events, Citations |
| psa-rotd50 | TimeSeriesMetadata, Motions, Events, Citations |
| psa-rotd100 | TimeSeriesMetadata, Motions, Events, Citations |
| psa-v | TimeSeriesMetadata, Motions, Events, Citations |
| sites | Vs30Codes |
| station-ssns | Stations, Collections |
| time-series-metadata | Motions, Citations, Events, Stations, Sites, Networks, Paths |
| time-series-metadata-versions | Versions, TimeSeriesMetadata |
| vs30-citations | Sites, Citations |
Multiple tables can be joined in the same request as a comma-separated list, e.g. "?join=Stations,Sites". At most 4 tables can be joined per request, and a single joined table can't be more than 4 tables deep along its own path (e.g. reaching Events from Eas goes through TimeSeriesMetadata and Motions - 3 tables deep, within the limit).
The time-series-metadata, intensity-measures, eas, psa-h1, psa-h2, psa-rotd0, psa-rotd50, psa-rotd100, and psa-v endpoints still apply the usual role-based restriction to publicly QA'd motions (see the "role" section below) regardless of what's joined in - a "user" role request only ever sees rows tied to a motion with public_motion = 1, the same as if no join were requested at all. The events, motions, and stations endpoints apply that same restriction too, but only when a request actually joins in one of those nine tables - e.g. "?join=TimeSeriesMetadata" on events restricts the results to events tied to a public motion, while requesting no such table (or being an admin/modeler) is unaffected.
Once a table is joined, its fields can also be used with the "where" and "sort" query string parameters, exactly as if those fields belonged to the endpoint itself. For example, "?join=Stations&where=station_code=CE12345" filters events by a field on the joined Stations table.
Foreign key fields are automatically left out of a joined table's data whenever the table they point to is also part of the request (including the endpoint's own table), since that table's own primary key field already provides the same information. For example, "?join=Stations,Sites" leaves out the site_id field that would otherwise appear from the stations table, since the sites table's own site_id field already covers it. If Sites weren't also requested, site_id would still appear, since it would otherwise be the only way to know which site a station belongs to.
When a table is joined, the "limit" query string parameter still controls the number of distinct records from the endpoint's own table, not the number of rows returned - a record with multiple related rows (e.g., an event with several ground motions) can still return more rows than the requested limit, since every one of that record's related rows is included.
Example Queries
Below are a few example queries that show how to specify endpoints and query string parameters.
This is not an exhaustive list, but just a few examples.
example 1
Query 20 events sorted in descending order by magnitude:
https://www.nga-gm.org/events?limit=20&sort=magnitude&direction=desc
example 2
Query 20 events sorted in descending order by magnitude where magnitude is between 5 and 6
https://www.nga-gm.org/events?limit=20&sort=magnitude&direction=desc&where=(magnitude>5.0+AND+magnitude<6.0)
example 3
Now add a where statement to exclude event_id values of 3, 5, and 10https://nga-gm.org/events?where=magnitude>5.0+AND+magnitude<6.0+AND+event_id+NOT+IN+(3,5,10)
example 4
Query 20 results for the rotd50 response_spectra component sorted in descending order by spectral acceleration at 0.1 second period:
https://www.nga-gm.org/psaRotd50?limit=20&sort=psa_rotd50_0p100&direction=desc
example 5
In some cases, users might need multiple queries to retrieve the data that they want. For example, a user might want to retrieve intensity measures for earthquakes with magnitude > 6. The magnitude field appears in the event table, but not in the intensity_measure table. So multiple queries are needed to retrieve the desired data. Specifically, we first need to retrieve event_id's with the desired magnitude range. We then need to retrieve the associated motion_ids from the motion table, and then the associated time_series_metadata_id's from the time_series_metadata table. Finally, we can retrieve the associated intensity measures. Such requests generally involve long lists of fields, and the POST method should therefore be used instead of the GET method. The Python code below shows how to perform the sequential queries necessary to retrieve the desired data.
import pandas as pd
import requests
import json
headers = {"Accept":"application/json","Authorization": "Bearer {}".format(token)}
event_url = 'https://www.nga-gm.org/events'
event_data = {'limit': 5000, 'where': 'magnitude>6', 'select': 'event_id'}
event_r = requests.post(event_url, headers=headers, json=event_data)
event_df = pd.DataFrame.from_dict(json.loads(event_r.text))
event_ids = event_df.event_id.values
motion_url = 'https://www.nga-gm.org/motions'
motion_data = {'limit': 5000, 'select': 'motion_id', 'where': 'event_id+IN+(' + ','.join(map(str,event_ids)) + ')'}
motion_r = requests.post(motion_url, headers=headers, json=motion_data)
motion_df = pd.DataFrame.from_dict(json.loads(motion_r.text))
motion_ids = motion_df.motion_id.values
time_series_metadata_url = 'https://www.nga-gm.org/timeSeriesMetadata'
time_series_metadata_data = {'limit': 5000, 'where' : 'motion_id+IN+(' + ','.join(map(str,motion_ids)) + ')'}
time_series_metadata_r = requests.post(time_series_metadata_url, headers=headers, json=time_series_metadata_data)
time_series_metadata_df = pd.DataFrame.from_dict(json.loads(time_series_metadata_r.text))
time_series_metadata_ids = time_series_metadata_df.time_series_metadata_id.values
intensity_measure_url = 'https://www.nga-gm.org/intensityMeasures'
intensity_measure_data = {'limit': 5000, 'where' : 'time_series_metadata_id+IN+(' + ','.join(map(str,time_series_metadata_ids)) + ')'}
intensity_measure_r = requests.post(intensity_measure_url, headers=headers, json=intensity_measure_data)
intensity_measure_df = pd.DataFrame.from_dict(json.loads(intensity_measure_r.text))
example 6
Query events joined with their station data, filtering on a field from the joined Stations
table (see Joining Related Tables above):
https://www.nga-gm.org/events?join=Stations&where=station_code=CE12345