Skip to content
TruTecDevelopers
Get an API key

Condition ratings (PASER & PCI)

Rate pavement condition from one image or a location and get a PASER rating or a PCI score with the distresses found.

View as Markdown

Send an image, or a location for TruTec to pull aerial imagery for, and get back a PASER rating (1 to 10) or a Pavement Condition Index (0 to 100). Each result lists the distresses found and a recommended treatment.

Ratings follow PASER and ASTM D6433. They are AI estimates from imagery: a screening-level rating, not a field survey.

A rating usually takes 10 to 20 seconds. Set your HTTP timeout to at least 90 seconds.

Endpoints#

Rate PASER#

POST /v1/condition/paser

Permission: Read measurements

Rate PCI#

POST /v1/condition/pci

Permission: Read measurements

Both endpoints take the same fields, as JSON or as multipart/form-data for a file upload. In a form, send polygon as a JSON string.

Send one image source. If you send an image, location fields are ignored. Otherwise TruTec uses the first of polygon, lat + lng, address.

Name Type Required Description
image file or string One image source Your own photo: JPEG, PNG or WebP, up to 15 MB and 60 megapixels, at least 64 px. As a string, plain base64 or a data:image/...;base64, URI. Aerial, drone and ground photos all work.
image_url string One image source A public https:// URL to an image (up to 2,000 characters).
polygon array or object One image source The area to rate, as [[lat, lng], ...] (3 or more points) or a GeoJSON Polygon or Feature (GeoJSON order [lng, lat]). TruTec frames the aerial to it and rates only pavement inside it. Recommended when you have a boundary.
lat, lng number One image source Center point for the aerial.
address string One image source Street address, geocoded to a center point.
imagery_source string No google (default) or nearmap, when TruTec fetches the aerial. Nearmap uses the newest Nearmap survey at the point and needs a plan that includes Nearmap.
zoom integer No 16 to 21. By default, fits the polygon (up to 20), or 20 for a point.
pavement_type string No auto (default), asphalt or concrete.
notes string No Context for the rating, up to 1,000 characters, e.g. "Seal coated 2021, heavy truck traffic at the dock".
return_image boolean No Return the exact JPEG that was rated in image.jpeg_base64, with the polygon outlined. Not available with image_url.

One call rates one frame. A polygon up to about 120 m (400 ft) across is rated at full detail. Larger polygons are fitted at a lower zoom and lose crack-level detail, so split large properties into sections.

curl https://trutec.ai/api/v1/condition/pci \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "polygon": [[30.40112, -97.72231], [30.40109, -97.72058], [30.39985, -97.72061], [30.39988, -97.72236]],
    "imagery_source": "nearmap"
  }'

Rate your own photo:

curl https://trutec.ai/api/v1/condition/paser \
  -H "X-API-Key: sk_your_api_key_here" \
  -F "[email protected]" \
  -F "pavement_type=asphalt"

Python:

import os
import requests

resp = requests.post(
    "https://trutec.ai/api/v1/condition/paser",
    headers={"X-API-Key": os.environ["TRUTEC_API_KEY"]},
    json={"lat": 30.4005, "lng": -97.7215, "imagery_source": "nearmap"},
    timeout=120,
)
resp.raise_for_status()
result = resp.json()
print(result["paser"]["rating"], result["paser"]["label"], result["image"]["capture_date"])

Response#

Use paser.rating or pci.score. Everything else explains it.

{
  "rating_type": "paser",
  "rubric_version": "1.0",
  "paser": {
    "rating": 6,
    "label": "Good",
    "description": "Slight raveling and traffic wear; cracks 1/4\"–1/2\" open, some closer than 10 ft; ...",
    "recommended_treatment": "Crack fill and seal coat; sound structure.",
    "limited_by": [
      {"distress": "weathering", "severity": "medium", "density_pct": 70.0, "max_rating": 6, "rule": "Weathered surface, traffic wear"}
    ]
  },
  "pavement_type": "asphalt",
  "confidence": 0.6,
  "summary": "The lot is structurally sound with no visible potholes. The surface has oxidized to a light gray. A seal coat would help protect it.",
  "distresses": [
    {"type": "weathering", "label": "Weathering / oxidation", "severity": "medium", "density_pct": 70.0, "density_unit": "area", "where": "Most of the lot"},
    {"type": "raveling", "label": "Raveling", "severity": "low", "density_pct": 15.0, "density_unit": "area", "where": "Drive aisles"}
  ],
  "limitations": ["Tight or hairline cracks may not be visible at this resolution."],
  "image": {
    "source": "nearmap",
    "capture_date": "2026-03-14",
    "survey_id": "88f1e2a0-1234-5678-abcd-1234567890ab",
    "lat": 30.4004850,
    "lng": -97.7214700,
    "zoom": 19,
    "width": 1280,
    "height": 1280,
    "meters_per_pixel": 0.129,
    "outlined": true,
    "view": "aerial",
    "quality": "good",
    "visible_pavement_pct": 85
  },
  "request_id": "9b2f4c1e8a7d4e0f9c3b6a5d2e1f0a9b",
  "rated_at": "2026-10-12T15:04:11.512Z"
}

A PCI response has the same envelope with a pci block instead of paser:

"pci": {
  "score": 85,
  "label": "Satisfactory",
  "strategy": "Preventive maintenance",
  "color": "#4ade80",
  "recommended_treatment": "Crack fill, seal coat and spot repairs.",
  "method": "astm_d6433",
  "deducts": [
    {"distress": "weathering", "severity": "medium", "density_pct": 75.0, "deduct_value": 11.3}
  ]
}
Field Description
paser.rating PASER rating, 1 to 10, with label, description and recommended_treatment.
paser.limited_by The distresses holding the rating at this level. Empty when overall surface condition sets the rating.
pci.score PCI, 0 to 100.
pci.label, strategy, color PCI band: Good 86–100, Satisfactory 71–85, Fair 56–70, Poor 41–55, Very poor 26–40, Serious 11–25, Failed 0–10.
pci.method astm_d6433 for asphalt; visual_estimate for concrete.
pci.deducts The deduct value for each distress (asphalt).
pavement_type asphalt or concrete.
confidence 0 to 1, reflecting image quality and resolution.
summary Two or three plain sentences for a property manager.
distresses[] type, label, severity (low, medium, high), density_pct, density_unit and a plain-language where.
distresses[].density_unit area = % of rated pavement; linear = linear feet per 100 sq ft; pothole_area = % of area that is pothole; slabs = % of concrete slabs.
limitations What the image could not show.
image.source google, nearmap, upload or url.
image.capture_date The Nearmap survey's capture date. null for Google, which does not publish dates. Not present for your own images.
image.survey_id The Nearmap survey used. null for Google.
image.view, quality, visible_pavement_pct aerial, ground or other; good, fair or poor; how much pavement could be seen.
request_id Quote this when contacting support.
rated_at When the rating ran (UTC).

Accuracy#

  • Satellite imagery cannot resolve hairline cracks, so aerial ratings can read high on lots with fine cracking. Nearmap, drone imagery or ground photos show more.
  • Crack width and rut depth can't be measured from above. Their severity is judged from what is visible and noted in limitations.
  • Cars, shadows and trees hide pavement. visible_pavement_pct and confidence show how much could be assessed.
  • Aerial imagery may be months or years old. Use Nearmap for a dated capture, or upload a current photo.

Usage#

Each successful rating counts as one call on your API key. Errors are not counted.

Errors#

Errors return {"error": "<message>", "code": "<code>"}. 502 and 503 are safe to retry.

Status code When
400 missing_image No image and no location sent.
400 invalid_request Bad polygon, lat/lng, zoom, imagery_source, pavement_type or image_url.
400 invalid_image The image is empty, can't be decoded, or is under 64 px.
403 forbidden The key lacks Read measurements.
403 upgrade_required Nearmap requested on a plan without Nearmap.
413 image_too_large Over 15 MB or 60 megapixels.
422 no_pavement_visible Nothing ratable in the image.
422 no_imagery_coverage Nearmap has no survey at that location. Retry with google.
422 address_not_found The address could not be found.
422 image_rejected The image could not be rated. Try a clearer or closer image.
500 imagery_unavailable Imagery is not configured on the server.
502 imagery_unavailable, geocode_failed, rating_failed A step failed. Retry.
503 service_unavailable Temporarily unavailable. Retry after a minute.

Authentication errors (401), plan errors (402/403) and usage-limit errors (429) use the standard API error format without code.