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 is

https://www.nga-gm.org
Submitting 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
The query string parameters are reasonably intuitive, perhaps with the exception of applying multiple conditions, the equal sign (=) for float fields, IN and NOT IN, and LIKE and NOT LIKE, which are discussed in more detail below.

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
Consider the examples below in which we search on a field called "event_name". Note that the wildcard character in the SQL query is the percent sign '%'. We use * instead of % in the query string because the percent sign is used in URL encoding and would result in potential errors.
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 isn't open to the public yet. For now it is limited to admins and modelers, who add role=modeler or role=admin to the URL (for example https://nga-gm.org/selection-scaling?role=modeler); other requests get status 403.

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 10
https://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

Try It in the Browser

You must be logged in to try live requests in the browser. Log in to use this panel, or continue reading below to use the API from your own script.

Possible Problems

The database is pretty complicated and there may be bugs in the code. If so, please contact us. Here is a list of a few things to watch out for.

Requesting huge amounts of data

There is a limit to the amount of data that can be returned by the server. You will receive an error message if you request an amount of data that exceeds the limit. In that case, you will need to retrieve the data in batches using the "page" query string parameter.

role

When users register for an account, they are assigned a "user" role. Additional roles are reserved for administrators and model developers. The user role has restricted access to the following tables: [motions, time_series_metadata, intensity_measures, response_spectra, fourier_spectra], and full access to all other tables. This is because the database team needs to perform QA / QC on the motions before making them publicly available, which is handled through the public_motion field in the motions table.

Floats/Doubles and =, IN, NOT IN

Using an equal sign or the IN or NOT IN operators to filter float-valued fields may return unexpected results due to floating point precision issues. For example, 0.1 might not actually be 0.1000000000, but something like 0.10000000015. You might need to restrict float fields within a narrow range instead. However, there are some exceptions. The database permits storing numerical data in decimal format, which have a fixed structure. For example, latitudes and longitudes are stored using the SQL float(8,5) format. For those cases, it is OK to use the equal sign. You can find the data types for each field on the schema page.

Filters that return no data

You might get an empty table back if you request data that doesn't exist. For example, asking for events with magnitude larger than 10 is going to fail. Also, doing something like rotd50<0.1&rotd50>0.2 will return no data because it's impossible for a number to be smaller than 0.1 and larger than 0.2.