Overview of API2
LMFDB’s existing API was designed to be a very thin HTTP wrapper over the database core of LMFDB. As such, it requires low level knowledge of the structure of the database, provides no guarantees of stability of the interface and no guarantees or information about what is stored in the database. API2 was designed to provide a middleware layer providing some level of stability without enforcing a rigid design paradigm on the LMFDB developer community.
It works by providing “searcher” objects that describe packaged sets of search operations that are permitted. Each searcher then allows a set of possible query parameters, one or more of which are required to perform a valid search. Human readable descriptions of searchers and search parameters are provided through the API.
API2 versioning
API2 uses a three part version string major.minor.revision. It is expected that breaking changes would only be introduced on a version change. Current version is 1.0.0
API2 resources
API2 consists of a single resource
the /api2 resource is responsible for describing what searches may be performed through the API, detailing the valid parameters for each search and returning the data from a performed search
API2 requests
Since API2 is intended for returning data from the server only all requests should be issued as HTTP(S) GET requests. There are six end points
/api2/description/searchers - Describe the list of possible searchers
/api2/description/<searcher> - Describe the list of possible search parameters for a given searcher
/api2/inventory/<searcher> - Describe the list of possible fields that can be returned by a searcher
/api2/data/<searcher> - Perform a search using a given searcher. Query parameters taken from the searcher description are provided as an ampersand delimited query string to this endpoint. Non searching parameters are available and are all prepended by “_”
/api2/pretty/<other_path> - This takes a valid other API path and produces a human readable (just) HTML page from the data contained. The other path should be the part after “api2” in the URL
/api2/singleton/<path> - This is intended to provide a URL that allows canonical reference to a single element of the database. The format of the singletons is not standardised and should not in general be used for machine readable access
/api2/livepg/<database> - This is intended to replicate the behaviour of the existing API. By specifying a database name and then using a standard search string (as per the /data/<searcher> endpoint) a search is performed on the raw database. This is not intended for normal access, but for debugging and testing purposes only.
API2 singleton queries
Singletons are intended to be references to machine readable versions of single objects in the database and are supposed to be the same as the existing human readable versions of the same data. So for example /api2/singletons/EllipticCurve/Q/11/a/2 will give a machine readable version of the same curve as /EllipticCurve/Q/11/a/2
API2 search query strings
The description parts of API2 take all of their parameters through their URL. Searches are performed by passing a query string. The main part of an API2 searcher query is name=value keys representing values for possible search parameters obtained from the description endpoint. These have the simple obvious form for equality, for example /api2/data/elliptic_curves_q?rank=0&conductor=11 is a search for rank=0, conductor = 11 in the elliptic curves over Q. Searching for conditions other than simple equality are performed by prepending an operator immediately after the = symbol. Currently there are 3 operators
“>” - Test for greater than a value
“<” - Test for less than a value
“%” - Test if the value specified is in the list of values in the database
For example /api2/data/elliptic_curves_q?rank=0&conductor=>11 is a search for rank=0, conductor > 11 There are also other possible query parameters that are not directly related to searching, but to controlling the data returned. These are
_view_start - Record to start viewing the list of returned fields. Should be given the value returned in the view_next field of the information returned by a search to get the next “page” of the data
_max_count - Maximum number of records to return from this search. Will not exceed the maximum number specified on the server but will be respected if lower than that value. For example, set to 1 if you are merely interested in the existence or non-existence of records of the specified type
_fields - The fields that should be returned by the search. The valid lists of fields should be inferred from a call to /api2/inventory/<searcher>
_exclude - If set = 1, change the operation of the _fields key from being the fields that should be returned for found records to being fields that should be excluded from the return
API2 responses
All API2 responses are as JSON documents complying with the ECMA-404 standard. Compliance with RFC 8259 is attempted but not guaranteed. The outer layer of the response is the same for all API2 requests
{"api_version":"1.0.0",
"type":"API_SEARCHERS",
"key": "GLOBAL",
"built_at": "2010-04-20T20:08:21.634121",
"data": [...]}
api_version - major.minor.revision string value showing version of API2 interface that the response is complying with
type - String describing the type of response to be parsed from the “data” key. May be API_SEARCHERS - Data should be parsed as a list of searcher objects API_DESCRIPTIONS - Data should be parsed as description of a searcher object API_INVENTORY - Data should be parsed as a description of the fields that will be returned by a searcher API_RECORDS - Data should be parsed as a list of records requested by a search API_ERROR - Data should be parsed as an error object. Request failed
built_at - ISO8601 UTC date time string of when request was processed
key - string identifying operation, may be GLOBAL
data - Valid ECMA-404 JSON document containing the detailed response
API_SEARCHERS
If the type field of the API2 response is “API_SEARCHERS” then the key field will be GLOBAL. The data field will consist of a JSON object the keys of which correspond to unique named searchers. The names are used when querying or using the searcher. Each value for these keys has the following format
{"elliptic_curves_q":{
"human_name":"Elliptic curves over q",
"desc":"Search elliptic curves defined over the rationals"}
}
human_name - Human readable name that should be presented to a user when selecting search options
desc - Human readable description of the purpose of the searcher. Should be presented to a user if the search source has a way of displaying extended text descriptions
API_DESCRIPTIONS
If the type field of the API2 response is “API_DESCRIPTIONS” then the key field will be the name of the searcher requested. The data field will consist of a JSON object with keys corresponding to the names of all available query fields for this searcher. A single key (representing a single query field) has the following structure.
{ "cm": {
"description": "CM code. 0 (for no CM), or a negative discriminant",
"example": "-163",
"type": "integer",
"cname":"CMCODE",
"codec":"CODEC_TO_COME"
}
}
It is important to note that a searcher is valid if it contains any subset of these fields, or is also valid if it is null.
description - Human readable description that should be presented to a user from clients that have the ability to display long form text when a search is being selected
example - An example of the record that would be a valid search term. This is especially important if there is no
codecfield because the user will have to provide a valid code manuallytype - This is a human readable representation of the type of the data that would be valid as a search. It may be auto generated or written by a human, so should not be parsed for type information
canonical_name - The canonical name of the item. This name should be unique and unchanging. If a client wants a given mathematical property then it should search for this property by canonical name and then use the name of the query field having the correct canonical name to actually search for the data. This allows substantial changes to the underlying representation of the data while retaining API layer compatability
codec - The codec information encoding the type of the data that is expected for this field
API_INVENTORY
If the type field of the API2 response is “API_INVENTORY” then the key field will be the name of the searcher requested. The data field will consist of a JSON object with keys corresponding to the names of all available return fields for this searcher. A single key (representing a single query field) has the following structure.
{ "cm": {
"description": "CM code. 0 (for no CM), or a negative discriminant",
"example": "-163",
"type": "integer",
"cname":"CMCODE",
"codec":"CODEC_TO_COME"
}
}
It is important to note that a return field is valid if it contains any subset of these fields, or is also valid if it is null.
description - Human readable description that should be presented to a user from clients that have the ability to display long form text if requested by a user
example - An example of the record that will be returned. This should be shown to a user as an example of what the field will return. This is especially important if there is no
codecfield because the user will have to parse the result manuallytype - This is a human readable representation of the type of the data that will be returned by the search. It may be auto generated or written by a human, so should not be parsed for type information
canonical_name - The canonical name of the item. This name should be unique and unchanging. If a client wants a given mathematical property then it should search for this property by canonical name and then use the name of the query field having the correct canonical name to actually search for the data. This allows substantial changes to the underlying representation of the data while retaining API layer compatability
codec - The codec information encoding the type of the data that is expected for this field
You will notice that the returns of API_DESCRIPTIONS and API_INVENTORY are often very similar, but this is not guaranteed. In the simple case the list of items that you can search on and fields that you will be returned will be the same but in general both must be parsed by a client.
API_RECORDS
If the type field of the API2 response is “API_RECORDS” then the key field will be a string containing the “data/” followed by the name of the searcher that generated the records. The data field will be a JSON object containing information about the number of records found in total, the number returned in this request and how to request the next tranche of records. It also contains a list of JSON objects returning the results from the database. The response has the following format
{"record_count":6000,
"view_start":0,
"view_count":100,
"view_next":99,
records:{...}
}
record_count - Total number of records returned by the search
view_start - First record returned in this “view” of the data. First element is zero
view_count - Number of records in this “view” of the data.
view_next - Offset that should be issued next request to move the view to the next set of records. Will be negative if you have reached the end of the available data
records - List of JSON objects containing data from the database. This is guaranteed to be a list of valid JSON object, but the data contained is not guaranteed in any way
Interpreting the records
The fields returned by an API_RECORDS object have no general form, but the /api2/inventory/
API_ERROR
If the type field of the API2 response is “API_ERROR” then the key field will be a string containing the string “GLOBAL”. The data field will be a string containing additional information about the error. A blank string is valid, but should not normally be returned. Your request has failed.
An example API2 client operation
This is an example of how a valid client would interact with API2
Find searchers
Client would connect to /api2/description/searchers. Result is
[
{"api_version":"1.0.0",
"type":"API_SEARCHERS",
"key": "GLOBAL",
"built_at": "2010-04-20T20:08:21.634121",
"data": [
{""elliptic_curves_q":{
"human_name":"Elliptic curves over q",
"desc":"Search elliptic curves defined over the rationals"}
},
{"classical_modular_forms":{
"human_name":"Classical Modular Forms",
"desc":"Search classical modular forms"}
}]
(In a fully working implementation there would be many more than this, but a client should cope with what is available). The client decides that it wishes to search in elliptic_curve_q.
Query searcher
Client connects to /api2/description/elliptic_curves_q. Result is
{"api_version":"1.0.0",
"type":"API_DESCRIPTIONS",
"key": "description/elliptic_curves_q",
"built_at": "2010-04-20T20:08:21.634121",
"data": [
...
...
"label":"{
"description": "Cremona label",
"example": "100002a1",
"type": "string",
"cname":"CREMONA_LABEL"
},
"lmfdb_label": {
"description": "LMFDB label",
"example": "100.a1",
"type": "string"
"cname":"LMFDB_LABEL"
},
...
...
]
}
The client parses this response and knows that it seeks to search on the canonical CREMONA_LABEL so it checks every returned search query term to ensure that a searchable field has a matching canonical name. It finds that a field does so and may continue. If searching on multiple fields this check should be performed on each field. Since the server will allow searching on canonical name (which should be unchanging by construction) it is acceptable for a client to skip this step and simply seek to search on a known canonical name but this is not recommended.
(Optional) Get inventory information
If a client is purely recovering data for a human to manage then this step can be skipped, but a typical client will get inventory information about the data returned by a search on the selected searcher.
Client connects to /api2/inventory/elliptic_curves_q. Result is
{"api_version":"1.0.0",
"type":"API_INVENTORY",
"key": "inventory/elliptic_curves_q",
"built_at": "2010-04-20T20:08:21.634121",
"data": [
"2adic_index": {
"description": "index in GL(2,Z2) of the 2-adicrepresentation (or 0 for CM curves)",
"example": "1",
"type": "integer",
"cname":"2ADIC_INDEX"
},
"2adic_label": {
"description": "Rouse label of the associated modular curve (null for CM curves). Based on Rouse, Zureik-Brown classification",
"example": "X1",
"type": "string",
"cname":"2ADIC_LABEL"
},
...
...
]
}
client is interested in returning the key with canonical name 2ADIC_INDEX. It can express this as _fields="2ADIC_INDEX" .
Get results
Knowing what it wants to search on and what it wants returned the client connects to /api2/data/elliptical_curve_q?label="100a3"&_fields="2ADIC_INDEX". Result is
{"api_version":"1.0.0",
"type":"API_INVENTORY",
"key": "inventory/elliptic_curves_q",
"built_at": "2010-04-20T20:08:21.634121",
"data": [
"record_count":1,
"view_start":0,
"view_count":1,
"view_next":-1,
records:[{2adic_index:12}]]
}
It is important to note that returned database keys are not transformed into canonical name form. The client must use the server supplied mappings from canonical name to database name