Geosetta API Documentation


Welcome to the Geosetta API Documentation. Our platform offers unparalleled access to a wealth of public geotechnical data, empowering your projects with accurate and comprehensive subsurface information. By becoming a sponsor of Geosetta, you not only contribute to the advancement and accessibility of geotechnical data but also gain exclusive API access. This access allows for seamless integration of our extensive data repository into your systems, facilitating more informed decision-making and innovative solutions in your field. Sponsoring Geosetta is more than just a partnership; it's an opportunity to be at the forefront of geotechnical data utilization and to support the ongoing mission of making this valuable data openly accessible to professionals and researchers worldwide. Join us in this endeavor and unlock the full potential of geotechnical data with Geosetta.


Quick Navigation

DIGGS API

In addition to the endpoints below, Geosetta provides a dedicated DIGGS (Data Interchange for Geotechnical and Geoenvironmental Specialists) API for standardized geotechnical data exchange. The DIGGS API allows you to query, retrieve, and work with borehole data in the industry-standard DIGGS XML format.

Full DIGGS API documentation is available at: https://diggs.geosetta.org/api/docs


πŸ†• VLM Boring Log Extraction API

The VLM (Vision Language Model) extraction service accepts geotechnical boring log PDFs and returns structured JSON data extracted by a fine-tuned vision language model. The service classifies each page as boring log or not, sends boring log pages to a GPU-powered extraction endpoint, and assembles multi-page results grouped by boring ID.

POST /api/vlm/extract

Authentication: Required β€” X-API-Key header must be provided.

Request: multipart/form-data

Parameter Type Required Default Description
file File (PDF) Yes β€” PDF file to process (max 50MB, max 100 pages)
output_mode string No both per_page, combined, or both

Example Request (Python):


import requests
import zipfile
import json
import os

api_key = "your_api_key_here"
pdf_file_path = "boring_log_report.pdf"

# Send PDF for extraction
with open(pdf_file_path, "rb") as f:
    response = requests.post(
        "https://diggs.geosetta.org/api/vlm/extract",
        headers={"X-API-Key": api_key},
        files={"file": f},
        data={
            "output_mode": "both"
        }
    )

if response.status_code == 200:
    # Save and extract the zip file
    with open("results.zip", "wb") as f:
        f.write(response.content)

    with zipfile.ZipFile("results.zip", "r") as z:
        z.extractall("results")

    # Read the combined boring data
    for boring_dir in os.listdir("results/borings"):
        if boring_dir == "grouping.json":
            continue
        data_path = f"results/borings/{boring_dir}/{boring_dir}.json"
        if os.path.exists(data_path):
            with open(data_path) as f:
                data = json.load(f)
            info = data.get("point_info", [{}])[0]
            print(f"{boring_dir}: {info.get('location', 'Unknown')}")
else:
    print(f"Error: {response.status_code}")
    print(response.text)

Example Request (cURL):


curl -X POST "https://diggs.geosetta.org/api/vlm/extract" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@boring_log_report.pdf" \
  -F "output_mode=both" \
  --output results.zip

Response: application/zip file containing structured JSON results.

Zip structure for per_page mode:


pages/
β”œβ”€β”€ classification.json
β”œβ”€β”€ page_0/
β”‚   └── B-1.json
β”œβ”€β”€ page_3/        (non-boring pages are skipped)
β”‚   └── B-2.json

Zip structure for combined mode:


borings/
β”œβ”€β”€ grouping.json
β”œβ”€β”€ B-1/
β”‚   └── B-1.json
β”œβ”€β”€ B-2/
β”‚   └── B-2.json

Zip structure for both mode: Contains both pages/ and borings/ directories.

classification.json example:


{
    "total_pages": 63,
    "boring_log_pages": [34, 35, 36, 37, 38],
    "non_boring_log_pages": [0, 1, 2, 3, 4]
}

grouping.json example:


{
    "borings": {
        "B-1": {
            "pages": [34, 35, 36],
            "notes": []
        },
        "B-2": {
            "pages": [37, 38],
            "notes": ["page 38 had no boring_id, assigned by continuation"]
        }
    },
    "ungrouped_pages": []
}

B-1.json example:


{
    "point_info": [
        {
            "boring_id": "B-1",
            "project_name": "Highway Bridge Replacement",
            "location": "Stafford County, Virginia",
            "latitude": 38.348151,
            "longitude": -77.484592,
            "elevation": 145.6,
            "drilling_method": "3.25\" HSA w/ SPTs",
            "logged_by": "Russell Kanith/HDR",
            "date_started": "2017-04-27",
            "date_completed": "2017-04-27",
            "page_number": 1
        }
    ],
    "samples_spt": [ ... ],
    "samples_lab": [ ... ],
    "lithology": [ ... ],
    "groundwater": [ ... ]
}

Error Responses:

Status Code Condition
400 Invalid output_mode, non-PDF file, corrupt PDF, PDF too large (>50MB), too many pages (>100)
401 Missing or invalid API key
500 Classifier model failed to load

Performance Notes:

GET /api/vlm/health

Authentication: Not required.

Example Request:


curl "https://diggs.geosetta.org/api/vlm/health"

Response:


{
    "status": "ok",
    "classifier_loaded": true,
    "modal_endpoint": "https://ross-cutts--boring-log-vlm-boringlogvlm-extract.modal.run",
    "modal_status": "ok"
}
Field Values Description
status ok / degraded degraded if classifier not loaded
classifier_loaded boolean Whether the ResNet page classifier is ready
modal_status ok / cold / unreachable GPU endpoint status. cold means it will need warm-up time on next request


πŸ†• DIGGS Viewer API

Wraps a DIGGS XML file in a self-contained interactive HTML viewer. The returned HTML works completely offline β€” no server, no dependencies, just open in any browser.

Viewer features:

Also available as a web UI at diggs.geosetta.org/?app=diggs_viewer (no API key required).

POST /api/viewer/wrap

Authentication: Not required.

Request: multipart/form-data

Parameter Type Required Description
file File (.xml or .diggs) Yes DIGGS XML file (max 50MB, UTF-8 encoded)

Response: text/html β€” self-contained HTML viewer downloaded as {filename}_viewer.html

Example Request (Python):


import requests

# Upload a DIGGS XML file and get back a self-contained HTML viewer
with open("my_borings.diggs.xml", "rb") as f:
    response = requests.post(
        "https://diggs.geosetta.org/api/viewer/wrap",
        files={"file": f}
    )

if response.status_code == 200:
    with open("my_borings_viewer.html", "wb") as f:
        f.write(response.content)
    print("Viewer saved! Open my_borings_viewer.html in any browser.")
else:
    print(f"Error: {response.status_code}")
    print(response.json())

Example Request (cURL):


curl -X POST "https://diggs.geosetta.org/api/viewer/wrap" \
  -F "file=@my_borings.diggs.xml" \
  -o my_borings_viewer.html

Error Responses:

Status Code Condition
400 File must be .xml or .diggs
400 File too large (max 50MB)
400 File must be valid UTF-8 encoded XML
400 File does not appear to be a DIGGS XML document
500 Failed to build viewer

GET /api/viewer/health

Authentication: Not required.


{
    "status": "ok",
    "src_exists": true,
    "plotly_cached": true,
    "index_template": true
}


πŸ†• Geosetta Subsurface Summary API

Generate comprehensive Geosetta Subsurface Summary reports for any user-defined polygon. These reports compile geological data, soil information, nearby boreholes, and ML predictions into a professional PDF format.

Features:


import requests
import json

api_key = "your_api_key_here"

# Define your polygon (GeoJSON format)
polygon_geojson = {
    "type": "Polygon",
    "coordinates": [[
        [-78.4300, 38.1150],  # Northwest corner
        [-78.4250, 38.1150],  # Northeast corner
        [-78.4250, 38.1100],  # Southeast corner
        [-78.4300, 38.1100],  # Southwest corner
        [-78.4300, 38.1150]   # Close polygon
    ]]
}

# Request body
request_body = {
    "geojson": json.dumps(polygon_geojson),
    "format": "pdf"  # Options: "pdf" for PDF report, "json" for raw data
}

# Make the request
response = requests.post(
    "https://geosetta.org/web_map/api/generate_site_report/",
    json=request_body,
    headers={
        "Authorization": f"Bearer {api_key}",  # Or use "X-API-Key": api_key
        "Content-Type": "application/json"
    }
)

# Check the response
if response.status_code == 200:
    result = response.json()
    if result['status'] == 'success':
        if request_body['format'] == 'pdf':
            # Decode base64 PDF
            import base64
            pdf_data = base64.b64decode(result['pdf'])
            
            # Save to file
            with open('site_assessment_appendix.pdf', 'wb') as f:
                f.write(pdf_data)
            print(f"Appendix PDF saved! Size: {result['metadata']['size_bytes']} bytes")
            print(f"Filename: {result['metadata']['filename']}")
        else:
            # JSON data format
            print("Report data received:")
            print(f"Polygon area: {result['data']['polygon']['area']['acres']:.2f} acres")
            print(f"Geology: {result['data']['geology']['formation']}")
            print(f"Nearby boreholes: {result['data']['boreholes']['total_count']}")
else:
    print(f"Error: {response.status_code}")
    print(response.json())

Response Format (PDF mode):


{
    "status": "success",
    "pdf": "JVBERi0xLjQKJcfs...",  // Base64 encoded PDF
    "metadata": {
        "filename": "geosetta_site_assessment_appendix_20241205_143022.pdf",
        "size_bytes": 98765,
        "generation_time": 6.23,
        "report_version": "1.0"
    },
    "disclaimer": {
        "text": "This report is for preliminary planning purposes only...",
        "terms_url": "https://geosetta.org/terms",
        "must_accept": true
    }
}

Response Format (JSON mode):


{
    "status": "success",
    "data": {
        "polygon": {
            "centroid": {"lat": 38.1125, "lon": -78.4275},
            "area": {"acres": 123.45, "hectares": 49.98, "square_meters": 499776},
            "address": "Near Charlottesville, VA 22903",
            "elevation_ft": 485.2
        },
        "geology": {
            "formation": "Granite",
            "age": "Paleozoic",
            "major_components": ["Granite", "Gneiss"],
            "usgs_url": "https://ngmdb.usgs.gov/..."
        },
        "soils": {
            "soil_type": "Alfisols",
            "wrb_class": "Luvisols",
            "properties": {
                "clay_content": 25.5,
                "sand_content": 35.2,
                "organic_carbon": 1.8
            },
            "usda_soil_survey_url": "https://websoilsurvey.sc.egov.usda.gov/..."
        },
        "predictions": {
            "predictions_by_depth": [
                {
                    "depth_ft": 0,
                    "spt_n": "10-25",
                    "soil_description": "Medium dense sandy CLAY"
                },
                // ... more depth predictions to 50 ft
            ],
            "groundwater": {
                "depth_label": "Medium (10-50 ft)",
                "deviation_label": "Moderate (5-20 ft variation)"
            }
        },
        "boreholes": {
            "total_count": 15,
            "closest_distance_miles": 0.8,
            "all_boreholes": [
                {
                    "functional_key": "38.1234;-78.4321",
                    "distance_miles": 0.8,
                    "provider": "VDOT",
                    "depth_ft": 45,
                    "map_url": "https://geosetta.org/web_map/map/#18/38.1234/-78.4321"
                }
                // ... more boreholes
            ]
        },
        "metadata": {
            "collection_time": 5.89,
            "timestamp": "2024-12-05T14:30:22Z"
        }
    },
    "disclaimer": {
        "text": "This data is for preliminary planning only...",
        "terms_url": "https://geosetta.org/terms",
        "must_accept": true
    }
}

Appendix Contents Include:



API Endpoint to retrieve Historic Data around Radius:


  api_key = "your_api_key_here"

  # Your payload and API key
  json_payload = {
      "deliverableType": "Return_Historic_Data_In_Radius",
      "data": {
          "points": [
              {
                  "latitude":   39.21884440935613,
                  "longitude": -76.84346423232522,
                  "radius_m": 1000
              }
          ]
      }
  }
  
  # Make the request
  response = requests.post(
      "https://geosetta.org/web_map/api_key/",
      json=json_payload,  # Directly send the json_payload
      headers={
          "Authorization": f"Bearer {api_key}"  # Set the API key in the Authorization header
      }
  )
  
  # Check the response
  if response.status_code == 200:
      print("API request successful:")
      print(response.json())
      response_data = response.json()  # Convert the Response object to a dictionary
      points_in_radius = response_data['results']['points_in_radius']
      print(points_in_radius)
  
  else:
      print("API request failed:")
      print(f"Status code: {response.status_code}")
      print(response.text)
  

The server will respond with a JSON object like this:


{'message': 'Data processed successfully.', 'results': {'points_in_radius': {'type': 'FeatureCollection', 'features': [{'type': 'Point', 'coordinates': [-76.8445259, 39.22082777], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2208278 , -76.8445259 elev ft: 335.20\n

Total Depth: 25.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}, {'type': 'Point', 'coordinates': [-76.84413791, 39.22100078], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2210008 , -76.8441379 elev ft: 346.00\n

Total Depth: 5.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}, {'type': 'Point', 'coordinates': [-76.8453399, 39.22108078], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2210808 , -76.8453399 elev ft: 338.10\n

Total Depth: 20.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}]}}, 'disclaimer': 'The information provided by this API has been made available by various DOTs and other public agencies. The predictive models presented herein are based on machine learning tools applied to the data. The predicted subsurface conditions are a probabilistic model and do not depict the actual conditions at the site. No claim as to the accuracy of the data and predictive model is made or implied. No other representation, expressed or implied, is included or intended and no warranty or guarantee is included or intended in any Geosetta data or models. The User understands these limitations and that Geosetta is a tool for planning subsurface investigations and not a substitute for it. In no event shall Geosetta or the Data Owners be liable to the User or another Party for any incidental, consequential, special, exemplary or indirect damages, lost business profits or lost data arising out of or in any way related to the use of information or data obtained from the Geosetta.org website or API.'} {'type': 'FeatureCollection', 'features': [{'type': 'Point', 'coordinates': [-76.8445259, 39.22082777], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2208278 , -76.8445259 elev ft: 335.20\n

Total Depth: 25.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}, {'type': 'Point', 'coordinates': [-76.84413791, 39.22100078], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2210008 , -76.8441379 elev ft: 346.00\n

Total Depth: 5.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}, {'type': 'Point', 'coordinates': [-76.8453399, 39.22108078], 'properties': {'content': '

Geosetta History

\n

Source: MDOT

(lat,lng): 39.2210808 , -76.8453399 elev ft: 338.10\n

Total Depth: 20.0 ft

Available Deliverables:
\n

Generate Pointcloud

\n

Monthly Precipitation History NOAA

\n

Open Soils Map

\n \n

\n
\n View Borehole Log with RSLog\n

\n

Diggs File

'}}]}




API Endpoint to retrieve data predictions (Note you can predict up to 50 points per request with a maximum depth of 100 feet.):

New Feature: Groundwater Predictions

Each point now includes groundwater depth and seasonal variation predictions powered by machine learning models:


  api_key = "your_api_key_here"

  # Your payload and API key
  json_payload = {
      "deliverableType": "SPT_Point_Prediction",
      "data": {
          "points": [
              {
                  "latitude": 39.387080,
                  "longitude": -76.813480,
                  "depth": 50,
                  "surfaceelevation": None  # Optional: if not provided, will be calculated automatically
              },
              {
                  "latitude": 39.4,
                  "longitude": -76.8,
                  "depth": 30
                  # surfaceelevation will be calculated automatically if not provided
              }
          ]
      }
  }
  
  
  # Make the request
  response = requests.post(
      "https://geosetta.org/web_map/api_key/",
      json=json_payload,  # Directly send the json_payload
      headers={
          "Authorization": f"Bearer {api_key}"  # Set the API key in the Authorization header
      }
  )
  
  # Check the response
  if response.status_code == 200:
      print("API request successful:")
      print(response.json())
  else:
      print("API request failed:")
      print(f"Status code: {response.status_code}")
      print(response.text)

The server will respond with a JSON object like this:


{
    "message": "Data processed successfully.",
    "results": [
        {
            "point": {
                "latitude": 39.387080,
                "longitude": -76.813480,
                "depth": 50,
                "surfaceelevation": null,
                "minimum_distance_from_trained_point_(mi)": 2.13
            },
            "profiles": [
                {
                    "depth": 0,
                    "dominant_grain_size": "SAND",
                    "dominant_N_value": "10-25"
                },
                {
                    "depth": 1,
                    "dominant_grain_size": "SAND",
                    "dominant_N_value": "10-25"
                },
                // ... depth profiles continue for each foot up to the requested depth
                {
                    "depth": 50,
                    "dominant_grain_size": "CLAY",
                    "dominant_N_value": "26-41"
                }
            ],
            "groundwater": {
                "depth_category": 1,
                "depth_label": "Medium (10-50 ft)",
                "depth_probabilities": {
                    "shallow": 23.7,
                    "medium": 55.9,
                    "deep": 20.4
                },
                "deviation_category": 1,
                "deviation_label": "Moderate (5-20 ft variation)",
                "deviation_probabilities": {
                    "low": 44.9,
                    "moderate": 50.2,
                    "high": 4.9
                }
            }
        },
        {
            "point": {
                "latitude": 39.4,
                "longitude": -76.8,
                "depth": 30,
                "surfaceelevation": null,
                "minimum_distance_from_trained_point_(mi)": 1.87
            },
            "profiles": [
                {
                    "depth": 0,
                    "dominant_grain_size": "SILT",
                    "dominant_N_value": "1-9"
                },
                {
                    "depth": 1,
                    "dominant_grain_size": "SILT",
                    "dominant_N_value": "10-25"
                },
                // ... depth profiles continue for each foot up to the requested depth
                {
                    "depth": 30,
                    "dominant_grain_size": "SAND",
                    "dominant_N_value": "10-25"
                }
            ],
            "groundwater": {
                "depth_category": 0,
                "depth_label": "Shallow (≀10 ft)",
                "depth_probabilities": {
                    "shallow": 65.2,
                    "medium": 28.4,
                    "deep": 6.4
                },
                "deviation_category": 0,
                "deviation_label": "Low (≀5 ft variation)",
                "deviation_probabilities": {
                    "low": 72.1,
                    "moderate": 21.3,
                    "high": 6.6
                }
            }
        }
    ],
    "disclaimer": "The information provided by this API has been made available by various DOTs and other public agencies. The predictive models presented herein are based on machine learning tools applied to the data. The predicted subsurface conditions are a probabilistic model and do not depict the actual conditions at the site. No claim as to the accuracy of the data and predictive model is made or implied. No other representation, expressed or implied, is included or intended and no warranty or guarantee is included or intended in any Geosetta data or models. The User understands these limitations and that Geosetta is a tool for planning subsurface investigations and not a substitute for it. In no event shall Geosetta or the Data Owners be liable to the User or another Party for any incidental, consequential, special, exemplary or indirect damages, lost business profits or lost data arising out of or in any way related to the use of information or data obtained from the Geosetta.org website or API."
}





Description of image

  api_key = "your_api_key_here"

  # Your payload and API key
  json_payload = {
      "deliverableType": "Project_Link",
      "data": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": {
          "coordinates": [
            [
              [
                -123.119842,
                49.323426
              ],
              [
                -123.117503,
                49.323421
              ],
              [
                -123.117524,
                49.322477
              ],
              [
                -123.118871,
                49.322239
              ],
              [
                -123.120083,
                49.322294
              ],
              [
                -123.119842,
                49.323426
              ]
            ]
          ],
          "type": "Polygon"
        },
        "properties": {
          "Project Title": "RSLog Example Project #1 (Imperial) ",
          "Project Number": "EX20-001",
          "Client": "ABC Client Company Ltd."
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "coordinates": [
            -123.11768,
            49.32323
          ],
          "type": "Point"
        },
        "properties": {
          "Name": "AH20-4a",
          "Depth": "13 ft",
          "Groundwater Depth": "",
          "Project Ref.": "RSLog Example Project #1 (Imperial)  (EX20-001)"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "coordinates": [
            -123.11934,
            49.32298
          ],
          "type": "Point"
        },
        "properties": {
          "Name": "testy",
          "Depth": "1 ft",
          "Groundwater Depth": "",
          "Project Ref.": "RSLog Example Project #1 (Imperial)  (EX20-001)"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "coordinates": [
            -123.11843,
            49.3227
          ],
          "type": "Point"
        },
        "properties": {
          "Name": "AH21-01",
          "Depth": "38.5 ft",
          "Groundwater Depth": "",
          "Project Ref.": "RSLog Example Project #1 (Imperial)  (EX20-001)"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "coordinates": [
            -123.11978,
            49.32242
          ],
          "type": "Point"
        },
        "properties": {
          "Name": "AH20-3",
          "Depth": "30 ft",
          "Groundwater Depth": "16.2 ft",
          "Project Ref.": "RSLog Example Project #1 (Imperial)  (EX20-001)"
        }
      }
    ]
  }
  }
  
  
  # Make the request
  response = requests.post(
      "https://geosetta.org/web_map/api_key/",
      json={
          "json_data": json.dumps(json_payload)  # Encode the dictionary as a JSON string
      },
      headers={
          "Authorization": f"Bearer {api_key}"  # Set the API key in the Authorization header
      }
  )
  
  # Check the response
  if response.status_code == 200:
      print("API request successful:")
      print(response.json())
  else:
      print("API request failed:")
      print(f"Status code: {response.status_code}")
      print(response.text)

The server will respond with a JSON object like this:


{'message': 'Data processed successfully.', 'results': 'https://geosetta.org/web_map/map/jqM826', 'disclaimer': 'The information provided by this API has been made available by various DOTs and other public agencies. The predictive models presented herein are based on machine learning tools applied to the data. The predicted subsurface conditions are a probabilistic model and do not depict the actual conditions at the site. No claim as to the accuracy of the data and predictive model is made or implied. No other representation, expressed or implied, is included or intended and no warranty or guarantee is included or intended in any Geosetta data or models. The User understands these limitations and that Geosetta is a tool for planning subsurface investigations and not a substitute for it. In no event shall Geosetta or the Data Owners be liable to the User or another Party for any incidental, consequential, special, exemplary or indirect damages, lost business profits or lost data arising out of or in any way related to the use of information or data obtained from the Geosetta.org website or API.'}




API endpoint retrieves the distance from a pre-trained point. It's important to note that not all data on Geosetta has been included in the training process. Therefore, the distance provided is relative to the nearest location that has been used in our training dataset.


  api_key = "your_api_key_here"

  # Your payload and API key
  json_payload = {
      "deliverableType": "Distance_From_Trained_Point",
      "data": {
          "points": [
              {
                  "latitude":   39.387080,
                  "longitude": -76.813480,
                  "depth": 50
              },
              {
                  "latitude": 39.4,
                  "longitude": -76.8,
                  "depth": 30
              }
          ]
      }
  }
  
  
  # Make the request
  response = requests.post(
      "https://geosetta.org/web_map/api_key/",
      json={
          "json_data": json.dumps(json_payload)  # Encode the dictionary as a JSON string
      },
      headers={
          "Authorization": f"Bearer {api_key}"  # Set the API key in the Authorization header
      }
  )
  
  # Check the response
  if response.status_code == 200:
      print("API request successful:")
      print(response.json())
  else:
      print("API request failed:")
      print(f"Status code: {response.status_code}")
      print(response.text)

The server will respond with a JSON object like this:


{'message': 'Data processed successfully.', 'results': {'minimum_distance_from_trained_point_(mi)': 2.13}, 'disclaimer': 'The information provided by this API has been made available by various DOTs and other public agencies. The predictive models presented herein are based on machine learning tools applied to the data. The predicted subsurface conditions are a probabilistic model and do not depict the actual conditions at the site. No claim as to the accuracy of the data and predictive model is made or implied. No other representation, expressed or implied, is included or intended and no warranty or guarantee is included or intended in any Geosetta data or models. The User understands these limitations and that Geosetta is a tool for planning subsurface investigations and not a substitute for it. In no event shall Geosetta or the Data Owners be liable to the User or another Party for any incidental, consequential, special, exemplary or indirect damages, lost business profits or lost data arising out of or in any way related to the use of information or data obtained from the Geosetta.org website or API.'}

API endpoint to convert a pdf boring log to a DIGGS file.


  import requests
  import json
  
  api_key = "your_api_key_here"
  
  # Example PDF file path
  pdf_file_path = '/content/example_boring_log.pdf'
  
  # Your payload and API key
  json_payload = {
      "deliverableType": "Pdf_Extraction",
      "data": {
      }
  }
  
  # Make the request
  with open(pdf_file_path, 'rb') as pdf_file:
      response = requests.post(
          "https://geosetta.org/web_map/api_key/",
          files={'file': pdf_file},
          data={
              'json_data': json.dumps(json_payload)
          },
          headers={
              "Authorization": f"Bearer {api_key}"  # Set the API key in the Authorization header
          }
      )
  
  # Check the response
  if response.status_code == 200:
      print("API request successful:")
      # Save the zip file content to a file
      zip_file_path = '/content/processed_results.zip'
      with open(zip_file_path, 'wb') as zip_file:
          zip_file.write(response.content)
      print(f"Zip file saved to {zip_file_path}")
  else:
      print("API request failed:")
      print(f"Status code: {response.status_code}")
      print(response.text)

The server will respond with a JSON object like this:


  API request successful:
  Zip file saved to /content/processed_results.zip

πŸ†• VLM Extraction Issue Reporting API

Report incorrect VLM boring log extractions to help improve model accuracy. When the extraction service misreads values β€” wrong blow counts, shifted depths, missing layers β€” submit a report with the page images and extraction data. Reports are stored as training examples and used to fine-tune future model versions.

Issue reports can also be submitted directly from the Boring Log Extractor web interface using the Report Issue button next to each boring.

POST /api/vlm/report

Submit an issue report for an incorrect extraction. Attach the page image(s) for the boring and the per-page extraction JSON that the model produced. Use the same X-API-Key you used for the original extraction request.

Authentication: Required β€” X-API-Key header must be provided.

Request: multipart/form-data

Parameter Type Required Default Description
images File(s) (PNG) Yes β€” One or more page images, named page_{N}.png where N is the page number (max 20MB each)
extraction_jsons string (JSON) Yes β€” JSON object mapping page numbers to their extraction results: {"5": {...}, "6": {...}}
description string No β€” Description of what's wrong with the extraction
boring_id string No β€” Boring ID being reported (e.g. B-1)
filename string No β€” Original PDF filename

Example Request (Python):


import requests
import json

api_key = "your_api_key_here"

# Page images and extraction data from a previous /vlm/extract call
page_images = {
    5: open("pages/B-1_page_5.png", "rb").read(),
    6: open("pages/B-1_page_6.png", "rb").read(),
}

extraction_data = {
    "5": {"results": {"point_info": [{"boring_id": "B-1"}], "samples_spt": [...]}},
    "6": {"results": {"samples_spt": [...], "lithology": [...]}},
}

# Submit the report
response = requests.post(
    "https://diggs.geosetta.org/api/vlm/report",
    headers={"X-API-Key": api_key},
    files=[
        ("images", ("page_5.png", page_images[5], "image/png")),
        ("images", ("page_6.png", page_images[6], "image/png")),
    ],
    data={
        "extraction_jsons": json.dumps(extraction_data),
        "description": "SPT N-values are shifted down one row β€” 15 at 5ft should be at 10ft",
        "boring_id": "B-1",
        "filename": "geotech_report.pdf",
    },
)

if response.status_code == 200:
    report = response.json()
    print(f"Report submitted: {report['report']['report_id']}")
else:
    print(f"Error: {response.status_code}")
    print(response.text)

Example Request (cURL):


curl -X POST "https://diggs.geosetta.org/api/vlm/report" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "images=@pages/page_5.png" \
  -F "images=@pages/page_6.png" \
  -F 'extraction_jsons={"5": {"results": {}}, "6": {"results": {}}}' \
  -F "description=SPT N-values shifted down one row" \
  -F "boring_id=B-1" \
  -F "filename=geotech_report.pdf"

Response:


{
    "status": "created",
    "report": {
        "report_id": "20260316_022326_f637b054",
        "api_key_prefix": "lZnSEWjt...",
        "boring_id": "B-1",
        "filename": "geotech_report.pdf",
        "description": "SPT N-values are shifted down one row β€” 15 at 5ft should be at 10ft",
        "pages": [5, 6],
        "has_correction": false,
        "created_at": "2026-03-16T02:23:26.804417+00:00"
    }
}

Error Responses:

Status Code Condition
400 Missing images, invalid extraction_jsons JSON, image too large (>20MB), page number mismatch between images and JSON keys
401 Missing or invalid API key
403 Invalid API key or no credits remaining

PATCH /api/vlm/reports/{report_id}

Add or update a corrected JSON for a previously submitted report. Only the original submitter (matching API key) can update a report.

Authentication: Required β€” X-API-Key header must be provided (must match the key used to create the report).

Request: multipart/form-data

Parameter Type Required Description
report_id string (path) Yes The report ID returned from POST /api/vlm/report
corrected_json string (JSON) Yes The corrected extraction data

Example Request (Python):


import requests
import json

api_key = "your_api_key_here"
report_id = "20260316_022326_f637b054"

corrected = {
    "point_info": [{"boring_id": "B-1", "total_depth": "50 ft"}],
    "samples_spt": [
        {"depth_top": "5.0", "depth_bottom": "6.5", "n_value": "15"},
        {"depth_top": "10.0", "depth_bottom": "11.5", "n_value": "22"}
    ]
}

response = requests.patch(
    f"https://diggs.geosetta.org/api/vlm/reports/{report_id}",
    headers={"X-API-Key": api_key},
    data={"corrected_json": json.dumps(corrected)},
)

if response.status_code == 200:
    print("Correction submitted successfully")
else:
    print(f"Error: {response.status_code}")
    print(response.text)

Example Request (cURL):


curl -X PATCH "https://diggs.geosetta.org/api/vlm/reports/20260316_022326_f637b054" \
  -H "X-API-Key: YOUR_API_KEY" \
  -F 'corrected_json={"point_info": [{"boring_id": "B-1"}], "samples_spt": [...]}'

Response:


{
    "status": "updated",
    "report": {
        "report_id": "20260316_022326_f637b054",
        "api_key_prefix": "lZnSEWjt...",
        "boring_id": "B-1",
        "filename": "geotech_report.pdf",
        "description": "SPT N-values are shifted down one row",
        "pages": [5, 6],
        "has_correction": true,
        "corrected_json": "{...}",
        "status": "new",
        "created_at": "2026-03-16T02:23:26.804417+00:00"
    }
}

Error Responses:

Status Code Condition
401 Missing or invalid API key
404 Report not found, or API key does not match the original submitter

GET /api/vlm/reports/{report_id}

Retrieve a single report including metadata and per-page extraction JSONs.

Authentication: Required β€” X-API-Key header must be provided.

Example Request (cURL):


curl "https://diggs.geosetta.org/api/vlm/reports/20260316_022326_f637b054" \
  -H "X-API-Key: YOUR_API_KEY"

Response:


{
    "report_id": "20260316_022326_f637b054",
    "api_key_prefix": "lZnSEWjt...",
    "boring_id": "B-1",
    "filename": "geotech_report.pdf",
    "description": "SPT N-values are shifted down one row",
    "pages": [5, 6],
    "page_extractions": {
        "5": "{\"results\": {\"point_info\": [{\"boring_id\": \"B-1\"}]}}",
        "6": "{\"results\": {\"samples_spt\": [{\"depth\": \"10\"}]}}"
    },
    "has_correction": false,
    "corrected_json": null,
    "status": "new",
    "created_at": "2026-03-16T02:23:26.826979+00:00"
}

Response Fields:

Field Type Description
report_id string Unique report identifier
pages array Page numbers included in this report
page_extractions object Map of page number β†’ raw extraction JSON string
has_correction boolean Whether a corrected JSON has been submitted
corrected_json string or null The corrected data, if submitted via PATCH
status string new, reviewed, training, or resolved

Seismic Site Class Prediction (ASCE 7-22)

Predict the ASCE 7-22 seismic site class at any location in the continental US. The endpoint runs Geosetta's ML models to predict soil type and SPT N-values at 1-ft intervals from 0–100 ft, converts to shear wave velocity (Vs) using the Imai & Tonouchi (1982) correlation, computes VS30 via harmonic averaging, and assigns a site class ranging from A (hard rock) to E (soft soil).

Results include lower and upper bound site classes derived from the predicted N-value category ranges, plus a per-depth confidence score based on proximity to training data, geological context, and model agreement.

GET /web_map/api/site_class/<lat>/<lng>/

Authentication: Required — X-API-Key header or session login.

Parameter Type Description
lat float Latitude (decimal degrees)
lng float Longitude (decimal degrees)

Example Request (Python):


import requests

response = requests.get(
    "https://geosetta.org/web_map/api/site_class/38.307/-82.046/",
    headers={"X-API-Key": "your_api_key_here"}
)

data = response.json()
print(f"Site Class: {data['site_class']['lower_bound']} - {data['site_class']['upper_bound']}")
print(f"VS30: {data['vs30_ft_s']['average']} ft/s")
print(f"Confidence: {data['confidence']['average_percent']}%")

Example Request (cURL):


curl -H "X-API-Key: your_api_key_here" \
  "https://geosetta.org/web_map/api/site_class/38.307/-82.046/"

Example Response:


{
  "status": "success",
  "location": {
    "latitude": 38.307,
    "longitude": -82.046,
    "elevation_ft": 892.5,
    "formation": "Kanawha Formation",
    "soil_type": "Silt loam"
  },
  "site_class": {
    "lower_bound": "D",
    "upper_bound": "CD",
    "standard": "ASCE 7-22"
  },
  "vs30_ft_s": {
    "lower_bound": 723.45,
    "upper_bound": 1102.33,
    "average": 912.89
  },
  "profile": {
    "depth_ft": [0, 1, 2, "...", 100],
    "soil_type": ["CLAY", "CLAY", "SILT", "..."],
    "n_value_category": ["1-9", "1-9", "10-25", "..."],
    "n_lower": [1, 1, 10, "..."],
    "n_upper": [9, 9, 25, "..."],
    "vs_lower_ft_s": [318.37, 318.37, 636.12, "..."],
    "vs_upper_ft_s": [637.21, 637.21, 922.45, "..."]
  },
  "confidence": {
    "average_percent": 62,
    "per_depth": [65, 64, 63, "..."]
  },
  "disclaimer": "ML-predicted values for preliminary screening only. Not a substitute for site-specific investigation."
}

Response Fields:

Field Description
site_class.lower_bound Site class from lower-bound N-values (conservative)
site_class.upper_bound Site class from upper-bound N-values
vs30_ft_s Time-averaged shear wave velocity over top 100 ft (ft/s)
profile.depth_ft Depth array (0–100 ft at 1 ft intervals)
profile.soil_type Predicted soil type at each depth (CLAY, SAND, SILT, GRAVEL, ROCK)
profile.n_value_category Predicted SPT N-value range at each depth
profile.vs_lower_ft_s / vs_upper_ft_s Shear wave velocity bounds at each depth (Imai & Tonouchi 1982)
confidence.average_percent Overall prediction confidence (0–100)

Notes:


Vector Tiles — Borehole Data Streaming

Stream Geosetta's full borehole dataset (~500,000 points) into any mapping application using Mapbox Vector Tiles (MVT). This is the most efficient way to display Geosetta data in your own maps — the client only downloads visible tiles, and the binary protobuf format is far smaller than GeoJSON.

Compatible with: MapLibre GL JS, Mapbox GL JS, Deck.gl, QGIS, ArcGIS Online, Leaflet (via plugins), and any client that supports the MVT specification.

GET /web_map/tiles/{z}/{x}/{y}.pbf

Authentication: Required — API key via query parameter or Authorization header.

URL Pattern:


https://geosetta.org/web_map/tiles/{z}/{x}/{y}.pbf?api_key=YOUR_API_KEY

For map clients that set tile URLs as templates, append ?api_key=YOUR_API_KEY to the URL. Alternatively, pass Authorization: Bearer YOUR_API_KEY in request headers (supported by MapLibre GL JS via transformRequest).

Parameter Type Description
z integer (0–22) Zoom level
x integer Tile column
y integer Tile row

Tile Contents:

Each tile contains a single vector layer named boreholes with the following properties:

Property Type Zoom Description
fk string ≥8 Functional key (lat;lon) — use to query detailed data
src string ≥8 Data provider (e.g. VDOT, MDOT)
t string ≥8 trained or untrained
cnt integer <8 Cluster point count (low zoom only)
tcnt integer <8 Trained point count within cluster (low zoom only)

At zoom < 8, points are server-side clustered for performance. At zoom ≥ 8, individual borehole points are returned.

Example: MapLibre GL JS


var apiKey = 'YOUR_API_KEY';

// Add Geosetta borehole data as a vector tile source
map.addSource('geosetta-boreholes', {
    type: 'vector',
    tiles: ['https://geosetta.org/web_map/tiles/{z}/{x}/{y}.pbf?api_key=' + apiKey],
    minzoom: 0,
    maxzoom: 16
});

// Render individual boreholes (zoom >= 8)
map.addLayer({
    id: 'boreholes-points',
    type: 'circle',
    source: 'geosetta-boreholes',
    'source-layer': 'boreholes',
    minzoom: 8,
    paint: {
        'circle-radius': 5,
        'circle-color': ['match', ['get', 't'],
            'trained', '#2ecc71',
            'untrained', '#e67e22',
            '#999999'
        ],
        'circle-stroke-width': 1,
        'circle-stroke-color': '#ffffff'
    }
});

// Render clusters (zoom < 8)
map.addLayer({
    id: 'boreholes-clusters',
    type: 'circle',
    source: 'geosetta-boreholes',
    'source-layer': 'boreholes',
    maxzoom: 8,
    paint: {
        'circle-radius': ['step', ['get', 'cnt'],
            8, 10, 12, 50, 16, 200, 22
        ],
        'circle-color': '#3498db',
        'circle-opacity': 0.7
    }
});

// Click handler: fetch detailed data on click
map.on('click', 'boreholes-points', function(e) {
    var props = e.features[0].properties;
    var fk = props.fk;  // e.g. "37.658736;-78.124825"
    var parts = fk.split(';');

    // Fetch full borehole details from Geosetta
    fetch('https://geosetta.org/web_map/get-point-info/?lat=' +
          parts[0] + '&lng=' + parts[1])
        .then(r => r.json())
        .then(data => {
            new maplibregl.Popup()
                .setLngLat(e.lngLat)
                .setHTML('<b>' + props.src + '</b><br>' + fk)
                .addTo(map);
        });
});

Example: QGIS

Add a vector tile layer via Layer → Add Layer → Add Vector Tile Layer:


URL: https://geosetta.org/web_map/tiles/{z}/{x}/{y}.pbf?api_key=YOUR_API_KEY
Min Zoom: 0
Max Zoom: 16

Example: Python (requests)


import requests

api_key = "your_api_key_here"

# Fetch a single tile (zoom 12, covering central Virginia)
response = requests.get(
    "https://geosetta.org/web_map/tiles/12/1153/1585.pbf",
    headers={"Authorization": f"Bearer {api_key}"}
)

print(f"Tile size: {len(response.content)} bytes")
print(f"Content-Type: {response.headers['Content-Type']}")
# Parse with mapbox-vector-tile library:
# pip install mapbox-vector-tile
import mapbox_vector_tile
tile_data = mapbox_vector_tile.decode(response.content)
boreholes = tile_data.get('boreholes', {}).get('features', [])
print(f"Boreholes in tile: {len(boreholes)}")

Performance Notes:

Combining with Other Geosetta APIs:

Vector tiles provide the spatial index — use the fk (functional key) from tile features to query detailed data via other Geosetta endpoints: