This page describes the read-only REST API for querying data on Next Generation Liquefaction: authentication, available endpoints, and the query-string syntax used to filter, sort, select, and paginate results. The REST API provides an alternative to browsing and downloading data using the website. You can query data directly from any software package that supports HTTP requests, which is well-suited to developing end-to-end workflows. We provide examples below using Python and its "requests" package.
Every endpoint requires a bearer token, so that only authenticated users can pull data and so we can tell which data a given user is allowed to see. Request a token by sending your account's username or email and password as HTTP Basic Auth to:
GET https://nextgenerationliquefaction.org/users/api-token
GET is shown above since this endpoint doesn't need a request body
anyway (your credentials go in the Basic Auth header, not the body), but
POST works here too - see Request Method
below. A token is valid for 2 hours from the time it's issued.
After that, request a new one the same way - we suggest never hard-coding your
username and password directly into a script; prompt for them instead, as below.
import requests
from requests.auth import HTTPBasicAuth
import getpass
import json
email = input('email: ')
password = getpass.getpass('password: ')
url = 'https://nextgenerationliquefaction.org/users/api-token'
headers = {}
headers['User-Agent'] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 134.0.6998.118 Safari/537.36"
headers['Accept'] = "application/json"
r = requests.get(url, headers=headers, auth=HTTPBasicAuth(email, password))
token = json.loads(r.text)['token']
Include that token on every subsequent request as a bearer token:
headers = {}
headers['Accept'] = "application/json"
headers['User-Agent'] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 134.0.6998.118 Safari/537.36"
headers['Authorization'] = "Bearer {}".format(token)
r = requests.get('https://nextgenerationliquefaction.org/sites/api-index', headers=headers)
A missing, malformed, expired, or incorrectly-signed token returns HTTP 400 with an
errorMessage explaining why.
Every endpoint accepts GET, with all parameters - filtering, sorting,
selecting fields, and pagination - passed as URL query-string parameters. This is
the simplest option and works for most requests.
Every endpoint also accepts POST, with the same parameters sent as the
request body (form-encoded or JSON) instead of the query string - useful when a
where= or select= value is too long to comfortably fit in
a URL. If a parameter is given both ways on the same request, the POST body value
wins.
Here is an example request with some query string parameters, done as GET:
headers = {}
headers["Accept"] = "application/json"
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 134.0.6998.118 Safari/537.36"
headers["Authorization"] = "Bearer {}".format(token)
r = requests.get(
'https://nextgenerationliquefaction.org/sites/api-index?where=SITE_LAT>30 AND SITE_LAT<40&select=SITE_ID,SITE_NAME,SITE_LAT,SITE_LON',
headers=headers,
)
...and as POST, with those same parameters moved into the request body
via data= instead of as query string parameters - the URL itself stays just
the base endpoint, which is the point when select=/where=
get long (e.g. dozens of field names):
headers = {}
headers["Accept"] = "application/json"
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 134.0.6998.118 Safari/537.36"
headers["Authorization"] = "Bearer {}".format(token)
r = requests.post(
'https://nextgenerationliquefaction.org/sites/api-index',
headers=headers,
data={
'select': 'SITE_ID,SITE_NAME,SITE_LAT,SITE_LON',
'where': 'SITE_LAT>30 AND SITE_LAT<40',
},
)
Both return the identical result.
A request has three parts:
https://nextgenerationliquefaction.org/sites/api-index?limit=20&sort=SITE_NAME&direction=asc
\______________base_url______________/\___endpoint____/\_________query_string_______________/
https://nextgenerationliquefaction.org/sites/api-index (see the full list below)? followed by &-separated key=value pairs
Each endpoint corresponds to one database table and returns a plain JSON array of
row objects (not wrapped in a named key) - [{...}, {...}, ...]. The
"Review status" column is explained in the What Data is Returned (Review Status)
section. The tables below cover the app's main resources; the join
between two of them is exposed separately, in
Junction (many-to-many) endpoints.
| Data | Endpoint | Table | Review status |
|---|---|---|---|
| Sites | /sites/api-index | SITE | Standard |
| Field tests | /field-tests/api-index | TEST | Standard |
| Field observations | /field-observations/api-index | FLDO | Standard |
| Samples | /samples/api-index | SAMP | Standard |
| Specimens | /specimens/api-index | SPEC | Standard |
| Plasticity tests | /plasticity-tests/api-index | PLAS | Standard |
| Other tests | /other-tests/api-index | OTHR | Standard |
| Index tests | /index-tests/api-index | INDX | Standard |
| Density measurements | /density-measurements/api-index | RDEN | Standard |
| Consolidation tests | /consolidation-tests/api-index | CONG | Standard |
| Consolidation test stages | /consolidation-test-stages/api-index | CON_STGE | Standard |
| Triaxial tests | /triaxial-tests/api-index | TXG | Standard |
| Triaxial test stages | /triaxial-test-stages/api-index | TXS | Standard |
| Direct simple shear tests | /direct-simple-shear-tests/api-index | DSSG | Standard |
| Direct simple shear test stages | /direct-simple-shear-test-stages/api-index | DSSS | Standard |
| Particle size distributions | /grag/api-index | GRAG | Standard |
| Particle size distribution data | /grat/api-index | GRAT | Standard |
| Penetration test results (SPT blow counts) | /penetration-test-results/api-index | ISPT | Standard |
| Boreholes | /boreholes/api-index | BORH | Standard |
| Depth/layer descriptions | /depth-layer-descriptions/api-index | DETL | Standard |
| Stratigraphic layer descriptions | /stratigraphic-layer-descriptions/api-index | STRA | Standard |
| Test pits | /test-pits/api-index | TEPT | Standard |
| Ground water tests | /ground-water-tests/api-index | WATR | Standard |
| Other field tests | /other-field-tests/api-index | OTHF | Standard |
| General cone penetration tests | /general-cone-penetration-tests/api-index | SCPG | Standard |
| Cone penetration data | /cone-penetration-data/api-index | SCPT | Standard |
| General invasive tests | /general-invasives/api-index | GINV | Standard |
| Invasive test details | /invasive-details/api-index | GIND | Standard |
| General surface wave investigations | /general-surface-wave-investigations/api-index | GSWG | Standard |
| Surface wave tests | /surface-wave-tests/api-index | SWVG | Standard |
| Surface wave test details | /surface-wave-details/api-index | SWVD | Standard |
| Surface wave dispersion curves | /surface-wave-dispersion-curves/api-index | GSWD | Standard |
| Displacement vectors | /displacement-vectors/api-index | FLDD | Standard |
| Liquefaction manifestations | /liquefaction-manifestations/api-index | FLDM | Standard |
| Ground motion intensity measurements | /ground-motion-intensity-measurements/api-index | GMIM | Standard |
| Files (metadata only, not file contents) | /files/api-index | FILE | Special - see below |
| Events | /events/api-index | EVNT | None (always all rows) |
| Stations | /stations/api-index | STAT | None (always all rows) |
| Segments | /segments/api-index | SEGM | None (always all rows) |
| Intensity measures | /intensity-measures/api-index | IM | None (always all rows) |
| Ground motion types | /gmim-types/api-index | GMIM_TYPE | None (always all rows) |
| Ground motion components | /gmim-comps/api-index | GMIM_COMP | None (always all rows) |
| Citations | /citations/api-index | CITATION | None (always all rows) |
| Labs | /labs/api-index | LAB | None (always all rows) |
| Lab programs | /lab-programs/api-index | LAB_PROGRAM | None (always all rows) |
These endpoints expose the join between two of the resources above directly, rather
than a resource you'd normally query on its own. Most carry nothing but the two
foreign keys; a few (the *-files ones) also carry the file's caption.
Their "Review status" follows whichever side of the join actually has a review
workflow - see below.
| Joins | Endpoint | Table | Review status |
|---|---|---|---|
| Sites ↔ Files | /site-files/api-index | SITF | Standard |
| Field tests ↔ Files | /field-test-files/api-index | TESF | Standard |
| Field observations ↔ Files | /field-observation-files/api-index | FLDF | Standard |
| Samples ↔ Files | /sample-files/api-index | SAMF | Standard |
| Sites ↔ Citations | /site-citations/api-index | SITP | Standard |
| Field tests ↔ Citations | /test-citations/api-index | TESP | Standard |
| Field observations ↔ Citations | /observation-citations/api-index | FLDP | Standard |
| Lab programs ↔ Citations | /lab-program-citations/api-index | LAB_PROGRAMP | None (always all rows) |
| Lab programs ↔ Samples | /lab-programs-samples/api-index | LAB_PROGRAM_SAMP | Standard |
| Field tests ↔ Samples | /test-samples/api-index | SAMP_TEST | Standard |
| Parameter | Default | Description |
|---|---|---|
limit | 50 | Maximum number of rows to return. |
page | 1 | Which page of results to return, 1-indexed. |
sort | the table's primary key | Field name to sort by. |
direction | asc | asc or desc (case-insensitive). |
select | all fields | Comma-separated list of field names to return. |
where | (none) | Filter expression - see below. |
includeUnreviewed | false | Whether to include submitted-but-not-yet-reviewed rows - see below. Accepted (and silently ignored) on endpoints with Review Status marked "None" above. |
role | (none) | Set to admin to bypass review-status filtering entirely - see below. Only valid value is admin; omit it entirely for normal (non-admin) behavior. If you do not have admin privileges, an error message will be returned. |
There's no separate pagination metadata (no total row count, no "next page" field)
in the response - it's always a plain array. If you need every row, keep requesting
increasing page values with a fixed limit until you get
back fewer rows than you asked for.
where=
where= takes one or more conditions, each in the form
FIELD OPERATOR VALUE, joined with AND, OR,
and parentheses for grouping. A comma is treated the same as AND.
AND/OR must be uppercase.
| Field type | Valid operators |
|---|---|
| Numeric / date / datetime | = != > >= < <= IN NOT IN |
| Text | all of the above, plus LIKE NOT LIKE |
| Boolean | = != |
Examples:
where=SITE_LAT>30
where=SITE_LAT>30 AND SITE_LAT<40
where=SITE_LAT>30,SITE_LON<-100
where=SITE_NAME=Alameda OR SITE_ID=2
where=(SITE_LAT>30 AND SITE_LON<-100) OR SITE_NAME LIKE *Alameda*
where=TEST_TYPE IN (BORH,SCPG,TEPT)
For LIKE/NOT LIKE, use * as a wildcard for
any number of characters - it's converted to SQL's % automatically.
As a shorthand, any query parameter that isn't one of the reserved names above
(limit, page, sort, direction,
select, where, includeUnreviewed) is treated
as a plain equality filter and folded into where automatically, so
?SITE_NAME=Alameda and ?where=SITE_NAME=Alameda do the
same thing. This is convenient for a single simple filter, but for anything
involving AND/OR/operators other than equality, use
where= directly.
Floating-point fields are stored with the usual floating-point rounding behavior -
avoid =/!= on them (e.g. a stored 30.1 may not
compare equal to a literal 30.1 in the query string); use a small range
with >/< instead.
Every site's data goes through a review workflow before it's public: a contributor submits it, and two reviewers later mark it reviewed. The REST API respects that same process for endpoints marked "Standard" in the table above:
includeUnreviewed=1, or if you're an admin.A row belongs to exactly one field test, which belongs to exactly one site (or, for field-observation-based data, one field observation, which likewise belongs to one site) - so this check follows that chain up to the site automatically. You don't need to (and can't) filter on review status directly.
Files is the one exception: a single file can be attached to a site, a field test, a field observation, and/or a sample all at once. A file is returned only if every one of the things it's attached to passes the check above - it's hidden as soon as any single attachment isn't submitted/reviewed, even if others are.
Endpoints marked "None" in the tables above (Events, Stations, Segments, Intensity
measures, ground motion types/components, Citations, Labs, Lab programs, and the
Lab programs ↔ Citations junction) are shared reference data with no single
owning site, so every authenticated request sees every row -
includeUnreviewed has no effect on them.
The other junction endpoints follow whichever side of the join actually has a review workflow - e.g. Lab programs ↔ Samples is gated by the Sample's field test/site even though Lab programs themselves have no STAT/REVW of their own; a row is hidden as soon as that side fails the check, same as any other "Standard" endpoint.
Admins: if your account has admin permissions, pass
role=admin to bypass review-status filtering entirely - every row is
returned regardless of STAT or REVW, including data that's never been submitted at
all. This has no effect on endpoints marked "None" above, since they're already
unfiltered. If you pass role=admin without actually having admin
permissions, the request fails with an error rather than silently falling back to
normal filtering - and admin status only ever applies when you explicitly ask for
it with role=admin; it's never assumed just because you happen to be an
admin.
Example 1 - the 20 southernmost reviewed sites:
https://nextgenerationliquefaction.org/sites/api-index?limit=20&sort=SITE_LAT&direction=asc
Example 2 - field tests of a couple of specific types, including unreviewed ones:
https://nextgenerationliquefaction.org/field-tests/api-index?where=TEST_TYPE IN (BORH,SCPG)&includeUnreviewed=1
Example 3 - only a couple of fields, filtered by a range:
https://nextgenerationliquefaction.org/sites/api-index?select=SITE_ID,SITE_NAME,SITE_LAT,SITE_LON&where=SITE_LAT>30 AND SITE_LAT<40
Full Python example, using the token from Authentication above:
import pandas as pd
import json
headers = {}
headers["Accept"] = "application/json"
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 134.0.6998.118 Safari/537.36"
headers["Authorization"] = "Bearer {}".format(token)
url = 'https://nextgenerationliquefaction.org/field-observations/api-index?limit=5000&includeUnreviewed=0'
r = requests.get(url, headers=headers)
df = pd.DataFrame.from_dict(json.loads(r.text))
[] when you expected data. Most often this
means the data hasn't been reviewed yet - try adding
includeUnreviewed=1 (this has no effect on the reference-data
endpoints, which always return everything).
errorMessage. The message
explains what's wrong (bad/expired token, unknown field name, invalid operator
for a field's type, etc.) - note that it may contain literal <br>
tags between multiple errors rather than newlines.
limit.
That's the last page - see the pagination note under
Query Parameters.
where= and/or
select= rather than pulling an entire table at once.