Data Sources API
The Data Sources API provides read and write access to sources, datasources and scans.
For the full reference, see Data Sources on apidocs.koordinates.com.
Source-specific notes
Section titled “Source-specific notes”There are several types of sources. Each exposes the same set of core fields, but some fields are only exposed or accepted for specific types of sources. Everything in options is optional.
Info sources
Section titled “Info sources”Info sources merely provide a way to point humans to some data. They are never scanned by Koordinates; they don’t have datasources; and you can’t create layers from them.
You can’t create info sources through the API (info is not a valid type). Existing info sources may still appear in source lists.
CIFS, URL, ArcGIS, and WFS source options
Section titled “CIFS, URL, ArcGIS, and WFS source options”All these source types can accept credentials in an options field:
options:username: username to access this server.password: password for this server.ssl_verify: A boolean specifying whether Koordinates should verify the SSL certificate of the host when scanning this source. Defaults totrue.
Username and password are write-only. They are never returned to clients via the API.
ArcGIS Server
Section titled “ArcGIS Server”-
url_remote: The URL of your ArcGIS server, usually ending with/rest/services/. Must not contain credentials. -
options: In addition to the options listed above, ArcGIS also accepts:folders: A JSON array of folders to scan. If not given, the whole server will be scanned.
url_remote: A URL of the form “cifs://host/sharename”, optionally with extra path segments. Must not contain credentials.
PostgreSQL
Section titled “PostgreSQL”-
url_remote: A URL of the form “postgresql://host:port/database”. Must not contain credentials. -
options:username: username to access the PostgreSQL database.password: postgres database password.schemas: A JSON array containing schema names, e.g.:"schemas": ["public", "other_schema"]
A URL source defines an FTP, HTTP or HTTPS url to a data file, or to an archive of data files, that Koordinates can scan.
Whether you can create URL sources depends on your site’s plan. If your plan doesn’t include them, creating one returns 400 with Source type 'url' is not available on this site's plan.
The URL can point straight at a single data file, such as a GeoPackage, CSV or shapefile, in any format Koordinates can import. It can also point at an archive containing data files. These archives are accepted:
- .zip
- .tar
- .tar.gz, .tgz
URL sources accept the following extra fields:
url_remote: The URL to download the file or archive from. Must not contain credentials.
When a URL source is scanned again, Koordinates compares the files’ latest modified time and total size with the last scan. For a .zip, that means the files inside it.
WFS Server
Section titled “WFS Server”url_remote: The URL to the WFS server endpoint. Must not contain credentials.
Amazon S3
Section titled “Amazon S3”S3 sources don’t use url_remote or options. They take these fields instead:
s3_bucket(required): the bucket name.s3_prefix: a key prefix to limit the scan to.s3_role_arn: an IAM role for Koordinates to assume. The read-onlys3_external_idfield gives the external ID to use in the role’s trust policy.s3_access_key_idands3_secret_access_key: access keys, as an alternative to a role, where your site allows them. The secret is never returned.s3_endpointands3_region: for S3-compatible storage. Not allowed withs3_role_arn.
When an S3 source is scanned again, Koordinates uses each object’s ETag to tell whether it has changed.
List and create sources
Section titled “List and create sources”/sources/List SourcesReturns a list of the sources available to you. The list supports page, page_size and sort (name, -name, created_at) and returns the X-Resource-Range and Link headers described in Introducing the Admin API.
Response 200
Headers
Content-Type: application/jsonBody
[ { "id": 1, "url": "https://{domain}/services/api/v1.x/sources/1/", "url_html": "https://{domain}/source/1-harbour-images/", "title": "harbour-images", "type": "upload", "featured_at": null, "datasource_count": 1, "user_capabilities": ["can-manage", "can-import"], "user_permissions": ["find", "view", "create_layers", "admin"] }]/sources/Create a SourceTo create a source, post to this view.
Parameters
Section titled “Parameters”- type:
string(required). The type of source to create. Example: upload. Choices:upload url arcgis s3 postgres wfs cifs. Which types you can create depends on your site’s plan. - title:
string(required). The name of the new source. (This is returned astitletoo.) - group:
string|id(required). The group that owns the source, by ID or API URL. Sources can only be owned by groups. SendinguserreturnsOnly groups can own this object. - description:
string(optional). - categories:
array(optional). - url_remote:
string(optional). The remote URL to the source, not required for upload or S3 sources - options: object (optional). Refer to the source specific notes above.
- scan_schedule:
string(optional). A Cron expression specifying how frequently the source should be scanned. All normal cron values are supported but the ‘minutes’ value is currently ignored. Predefined scheduling definitions are supported except for “@reboot”.
Request
Headers
Content-Type: application/jsonBody
{ "title": "Bob's ArcGIS 10 Server", "type": "arcgis", "description": "All of Bob's data", "categories": [], "group": 3, "options": { "username": "bob", "password": "sekret_p@55w0rd" }, "url_remote": "https://bob.example.com/ArcGIS/rest/services", "scan_schedule": "@daily"}Response 201
Headers
Content-Type: application/jsonLocation: https://{domain}/services/api/v1.x/sources/2/Body: the source detail, as in Source detail.
/sources/Create an upload sourceTo upload files to the API, use the multipart/form-data content-type instead of JSON.
Your multipart request should include one part with name="source", which contains the JSON object with the required fields (title, type and group), as per above. Other parts should include their filename in the Content-Disposition header.
Each file you upload can either be a normal file, or an archive containing more files. We currently support these archive types:
- .zip
- .tar.gz, .tgz
- .7z
Request
Headers
Content-Type: multipart/form-data; boundary=b0017bc3bab14fed95910bfcc4a1ddd8Body
--b0017bc3bab14fed95910bfcc4a1ddd8Content-Disposition: form-data; name="abc"; filename="points-of-interest.zip"Content-Type: application/x-zip
(binary data not shown)--b0017bc3bab14fed95910bfcc4a1ddd8Content-Disposition: form-data; name="source"Content-Type: application/json
{ "title": "points-of-interest", "type": "upload", "group": 3}--b0017bc3bab14fed95910bfcc4a1ddd8--Response 201
Headers
Content-Type: application/jsonLocation: https://{domain}/services/api/v1.x/sources/3/Body: the source detail. A scan starts automatically for multipart uploads.
Upload large files with presigned URLs
Section titled “Upload large files with presigned URLs”Instead of sending files in a multipart request, you can create an upload source with a JSON body that lists the file names in files, for example {"title": "roads", "type": "upload", "group": 3, "files": ["roads.zip"]}. To split a file into parts, add the part count in square brackets, for example "roads.zip[10]". Each entry in the response’s files list has a filename, a state (PENDING until you complete it) and parts, a presigned URL for each part.
-
PUTeach part to its presigned URL with noContent-Typeheader (aContent-Typethat wasn’t signed, such as theapplication/x-www-form-urlencodedthat some clients add, fails withSignatureDoesNotMatch), and keep theETagheader from each response. -
Complete each file by posting its ETags, in part order, as
{"etags": [...]}to/sources/{id}/uploads/{filename}/. The response is the file withstateset toCOMPLETED. -
Start a scan with
POST /sources/{id}/scans/. Presigned uploads don’t start a scan automatically.
/sources/{id}/uploads/List a source's presigned uploadsEach item has filename, state and pre_signed_urls.
/sources/{id}/uploads/{filename}/Complete a presigned uploadSource detail
Section titled “Source detail”/sources/{id}/Get source detailResponse 200
Body
{ "id": 1, "url": "https://{domain}/services/api/v1.x/sources/1/", "url_html": "https://{domain}/source/1-harbour-images/", "title": "harbour-images", "type": "upload", "featured_at": null, "datasource_count": 1, "user_capabilities": ["can-manage", "can-import"], "user_permissions": ["find", "view", "create_layers", "admin"], "license": null, "publish_to_catalog_services": false, "permissions": "https://{domain}/services/api/v1.x/sources/1/permissions/", "description": "Upload: harbour-images", "description_html": "<p>Upload: harbour-images</p>", "categories": [], "tags": [], "group": { "id": 3, "url": "https://{domain}/services/api/v1.x/groups/3/", "name": "Bob Jones and Associates", "country": "NZ", "type": "normal" }, "metadata": null, "last_scanned_at": null, "scan_schedule": null, "options": {}, "datasources": "https://{domain}/services/api/v1.x/sources/1/datasources/", "scans": "https://{domain}/services/api/v1.x/sources/1/scans/", "last_scan": null, "settings": { "lock_title_to_metadata": false, "lock_description_to_metadata": false, "lock_tags_to_metadata": false }, "files": [ { "filename": "harbour-images.zip", "parts": ["https://presigned-upload-url.example.com/…"], "state": "COMPLETED", "expires_at": "2026-10-17T01:11:19.697872Z" } ]}/sources/{id}/Edit a sourceNote that:
- You can’t change the type of a source.
- You can’t upload more files into an upload source. If you need to upload more files you should create a new source.
The following fields are editable (other fields will be ignored):
titledescriptioncategoriesgroupscan_scheduleoptions(note: existing options will be preserved if they’re not specified here)
Request
Headers
Content-Type: application/jsonBody
{ "description": "Harbour Imagery (4m/px)"}Response 200
The updated source detail, as above.
/sources/{id}/Delete a sourceResponse 204. A deleted source returns 404.
A ‘scan’ occurs when we inspect a source to find content that can be used to create layers, tables, and documents.
/sources/{id}/scans/List scansReturns a list of scans for a source.
Notable fields:
-
status: The current status of this scan. Valid values are:runningcompletedcancellederror
-
change_counts: counts of datasources deleted, updated and added in this scan. This is only guaranteed to be present in completed scans. For other scans it may be null or the counts may all be 0. -
last_activity_at: the last time the scan reported activity. -
source: the API URL of the source.
Response 200
Headers
Content-Type: application/jsonBody
[ { "id": 4, "url": "https://{domain}/services/api/v1.x/sources/1/scans/4/", "status": "completed", "started_at": "2026-10-16T01:11:38.937303Z", "completed_at": "2026-10-16T01:11:52.730702Z", "last_activity_at": "2026-10-16T01:11:52.730825Z", "change_counts": { "added": 1, "deleted": 0, "updated": 0 }, "log": "https://{domain}/services/api/v1.x/sources/1/scans/4/log/", "source": "https://{domain}/services/api/v1.x/sources/1/" }]/sources/{id}/scans/Start scanStarts a scan for this source. No post data is required. The scan runs in the background: the 201 response has status of running, and the Location header is the scan URL. Poll the scan until status is completed, cancelled or error.
Response 201
Headers
Content-Type: application/jsonBody
{ "id": 4, "url": "https://{domain}/services/api/v1.x/sources/1/scans/4/", "status": "running", "started_at": "2026-10-16T01:11:38.937303Z", "completed_at": null, "last_activity_at": "2026-10-16T01:11:52.730825Z", "change_counts": { "added": 0, "deleted": 0, "updated": 0 }, "log": "https://{domain}/services/api/v1.x/sources/1/scans/4/log/", "source": "https://{domain}/services/api/v1.x/sources/1/"}Scan detail
Section titled “Scan detail”/sources/{id}/scans/{scan}/Get scan detailInformation about a particular scan. This is the same as an item in the list view above, except it also includes a progress indicator, which is a float between 0 and 1. Progress is approximate.
Response 200
Headers
Content-Type: application/jsonBody
{ "id": 4, "url": "https://{domain}/services/api/v1.x/sources/1/scans/4/", "status": "completed", "started_at": "2026-10-16T01:11:38.937303Z", "completed_at": "2026-10-16T01:11:52.730702Z", "last_activity_at": "2026-10-16T01:11:52.730825Z", "change_counts": { "added": 1, "deleted": 0, "updated": 0 }, "log": "https://{domain}/services/api/v1.x/sources/1/scans/4/log/", "source": "https://{domain}/services/api/v1.x/sources/1/", "progress": 1.0}/sources/{id}/scans/{scan}/Cancel or delete scanCancels an in-progress scan.
Response 204
Scan log
Section titled “Scan log”/sources/{id}/scans/{scan}/log/Get scan logGets a log for a scan. It’s returned as text/plain when you send Accept: text/plain. With Accept: application/json the whole log is returned as one JSON string.
Response 200
Headers
Content-Type: text/plainBody
2012-11-14 15:53:28: Scraping ArcGIS server at https://bob.example.com/ArcGIS/rest/services2012-11-14 15:53:29: Looking for layers in folder: Bob/MapServer2012-11-14 15:53:29: Requesting access to https://bob.example.com/ArcGIS/rest/services/2012-11-14 15:53:29: get: https://bob.example.com/ArcGIS/rest/services/Bob/MapServer?f=json2012-11-14 15:53:29: crawled in 0.445s2012-11-14 15:53:29: Looking in folder: Bob/MapServer2012-11-14 15:53:29: Found layer: 0 - 'Null Island Polygons'2012-11-14 15:53:29: get: https://bob.example.com/ArcGIS/rest/services/Bob/MapServer/0?f=json2012-11-14 15:53:30: crawled in 0.447s2012-11-14 15:53:30: get: https://bob.example.com/ArcGIS/rest/services/Bob/MapServer/0/query?returnGeometry=false&outFields=%2A&returnIdsOnly=true&where=1%3D1&f=json2012-11-14 15:53:31: crawled in 0.601s2012-11-14 15:53:47: Finishing up2012-11-14 15:53:47: Updating 1 new or changed datasets2012-11-14 15:53:47: Updating user interface2012-11-14 15:53:47: Finished!Source tree
Section titled “Source tree”/sources/{id}/tree/Get the source's file treeReturns the tree of folders, files, datasources and metadata found by scanning the source, with counts at each node. Add a path after tree/ to get a node further down the tree, for example /sources/{id}/tree/folder/file.gpkg/.
Data sources
Section titled “Data sources”The API for datasources is read-only. Datasources are updated by scanning your source. If you don’t see what you expect here, check that your source has been scanned recently.
/sources/{id}/datasources/List datasourcesLists datasources for this source.
Response 200
Headers
Content-Type: application/jsonBody
[ { "id": 1, "url": "https://{domain}/services/api/v1.x/sources/1/datasources/1/", "title": "apidoc test points", "data_type": "vector", "data_type_label": "Vector", "format": "application/json", "geometry_type": "point", "updated_at": "2026-10-16T01:11:40.461348Z", "state": "new", "type": "upload", "upload_path": "apidoc-test-points.geojson" }]Datasource detail
Section titled “Datasource detail”/sources/{id}/datasources/{datasource}/Get datasource detailDetailed information for a given datasource. Includes all the info shown in the list above, as well as much more information describing the datasource.
Field notes
-
typeis the source type, not to be confused withdata_type,formatorgeometry_type -
stateis the datasource’s state, for examplenew -
layerlinks to the layer created from this datasource, or is null -
data_typerefers to the broad type of data. Different types of data require significantly different handling. Current data types are:tablevectorrastergridattribute-grid
-
formatis a string describing the media type of this datasource. -
geometry_typedescribes the type of geometry in vector datasources. It is null for non-vector datasources. Possible values are:pointlinestringpolygonmultipointmultilinestringmultipolygon
-
Spatial datasources may or may not have a known Coordinate Reference System (
crs). -
extentis a GeoJSON geometry. It may be null for aspatial datasources. The coordinate system for this geometry is the same as that in thecrsfield, which may not be known. -
For vector and table datasources,
fieldsis a list of JSON objects. It will be null for other datasources.
Response 200
Headers
Content-Type: application/jsonBody
{ "id": 1, "url": "https://{domain}/services/api/v1.x/sources/1/datasources/1/", "title": "apidoc test points", "data_type": "vector", "data_type_label": "Vector", "format": "application/json", "geometry_type": "point", "updated_at": "2026-10-16T01:11:40.461348Z", "state": "new", "type": "upload", "crs": "EPSG:4326", "fields": [ {"name": "GEOMETRY", "type": "geometry"}, {"name": "id", "type": "integer"}, {"name": "name", "type": "string"} ], "num_features": 3, "num_bands": null, "has_z": false, "extent": { "type": "Polygon", "coordinates": [ [ [174.76, -36.87], [174.76, -36.85], [174.8, -36.85], [174.8, -36.87], [174.76, -36.87] ] ] }, "source": "https://{domain}/services/api/v1.x/sources/1/", "metadata": null, "metadata_resource": null, "description": "", "geotags": "https://{domain}/services/api/v1.x/sources/1/datasources/1/geotags/", "upload_path": "apidoc-test-points.geojson", "layer": null}Other fields depend on the source type: for example ArcGIS datasources include arcgis_folder_service_path, arcgis_geometry_type, arcgis_layer_name and arcgis_type.
Datasource metadata
Section titled “Datasource metadata”/sources/{id}/datasources/{datasource}/metadata/Get datasource metadataSome datasources can have attached XML metadata. If a datasource has none, this returns 404. This is attached by the CIFS and Upload scanners, if a datasource has an .xml file with the same basename. For example:
my-points-with-metadata.geojsonmy-points-with-metadata.xmlAs with sources, datasource XML must be in one of these formats (sources and datasources, like documents and sets, accept ISO 19115/19139 and Dublin Core; FGDC is accepted for layers and tables):
- ISO 19115/19139
- FGDC CSDGM
- Dublin Core
Response 200
Body
<dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"> <title></title> <creator>Bob Jones and Associates</creator> <subject>hydro</subject> <description>Some nice colourful aerial photos of the Auckland Harbour.</description> <date>2013-01-01</date> <type>raster</type> <language>eng</language></dc>