PASAY CITY GISAPI guide · http://localhost/api/v1

Connect your system to Pasay City GIS

The API gives other systems the same data the map uses: the GIS input entries (public facilities, utilities, overpasses, bridges, businesses and parcels) with their attachments, the registered businesses per barangay, and the zone parcel layers with their tax declaration details. With a read and write key, a system can also add, edit and delete GIS input entries.

Base URLhttp://localhost/api/v1/
FormatJSON (map data as GeoJSON)
AuthAPI key in a header
Versionv1

Everything changed through the API shows on the map straight away, and is recorded in the platform's history under the key's account, marked via API key.

Getting a key

Keys are created by a platform administrator (a staff account):

  1. Open the map, click the gear ⚙ at the top right, then API keys (or go to /admin/pasay/apikey/).
  2. Click Add API key. Give it a name that says who uses it, e.g. City Hall ERP.
  3. Choose the account it acts as. Entries added or edited with the key are recorded under that account, so a dedicated account per system (e.g. erp-integration) keeps the history clear.
  4. Choose the access: Read only (fetch data) or Read and write (also add, edit and delete entries). Optionally set an expiry.
  5. Save. The key is shown once — copy it then. Only a fingerprint of it is kept, so a lost key can't be recovered: revoke it (select it in the list → Revoke selected keys) and add a new one.

A key looks like pasay_1a2b3c4d.Xy…. The part before the dot identifies it in the admin list.

Authentication

Send the key with every request, in either header:

Authorization: Bearer pasay_1a2b3c4d.your-secret-part
X-API-Key: pasay_1a2b3c4d.your-secret-part

Requests without a valid key get 401. Revoked keys, expired keys and keys whose account has been deactivated stop working immediately. Read only keys get 403 on POST, PUT, PATCH and DELETE.

Quick start

Check the key works — the service answers with the key's name and access:

export PASAY_GIS_API_KEY="pasay_1a2b3c4d.your-secret-part"

curl -H "Authorization: Bearer $PASAY_GIS_API_KEY" http://localhost/api/v1/

# Parcel/Lot entries in Zone 9, changed since 1 January
curl -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
     "http://localhost/api/v1/entries/?category=parcel&zone=9&updated_since=2026-01-01"

Responses & errors

Successful calls return 200 (or 201 when something was created) with JSON. Errors always have this shape:

{
  "error": {
    "code": "invalid",
    "message": "Please correct the highlighted fields.",
    "fields": { "details.acctno": ["This field is required."] }
  }
}
StatuscodeWhen
400invalidThe data didn't pass validation; fields says which fields and why. Field names match the request, with form columns as details.<key>, and attachment problems under files.
400invalid_json · invalid_parameterThe body isn't a JSON object, or a query parameter is malformed.
401invalid_keyNo key, an unknown key, or a revoked or expired one.
403read_only_keyA read only key tried to change data.
404not_foundThe entry, file, barangay, zone or parcel doesn't exist (or was deleted).
405method_not_allowedThat method isn't supported on that URL; the Allow header lists the ones that are.
429rate_limitedToo many requests; wait the seconds given in Retry-After.

Times are ISO 8601 with time zone, e.g. 2026-11-06T09:15:00+00:00. Coordinates are WGS 84 (longitude, latitude), as in GeoJSON.

Paging & limits

Lists are paged. Pass page (from 1) and page_size; every list response carries count, page, page_size, pages, and full next / previous URLs (null at the ends) — follow next until it's null.

ListDefault page sizeMaximum
Entries1001000
Zone parcels2002000
Businesses5005000

Rate limit: 120 requests per minute per key. Responses include X-RateLimit-Limit and X-RateLimit-Remaining; over the limit you get 429 with Retry-After. For syncing, prefer updated_since over re-reading everything.

Endpoints

Paths are relative to http://localhost/api/v1. GET works with any key; POST PATCH DELETE need a read and write key.

GET /

Checks the key and lists the main endpoints. Includes key.name, key.access (read or write), key.acts_as and key.expires_at.

GET /forms/   GET /forms/{category}/

The GIS input forms and their fields — the same definitions the platform validates against (public_facility, utility, overpass, bridge, business, parcel). Summarised under Form fields.

GET /entries/

GIS input entries, newest change first, as a GeoJSON FeatureCollection (plus the paging fields).

ParameterMeaning
categoryOne form: public_facility, utility, overpass, bridge, business, parcel
zoneZone number or id (9 or zone_9) — Parcel/Lot entries
pinA parcel's PIN — Parcel/Lot entries
parcel_refAn unsure parcel's reference (it has no PIN) — Parcel/Lot entries
qText in the entry's name
updated_sinceOnly entries changed at or after this date / date-time
page, page_sizeSee Paging

Each feature (an entry):

{
  "type": "Feature",
  "id": 42,
  "geometry": { "type": "Point", "coordinates": [120.998812, 14.541731] },
  "properties": {
    "id": 42,
    "category": "parcel",
    "category_label": "Parcel/Lot",
    "name": "123-01-086-03-010",
    "type": "Residential",
    "asset_type": "Residential", "asset_type_other": "", "condition": "",
    "details": { "arp_tdn": "C1-086-00173", "parcel_class": "Residential", "area": 64.0 },
    "zone": "zone_9", "pin": "123-01-086-03-010",
    "latitude": 14.541731, "longitude": 120.998812,
    "created_by": "Juan Dela Cruz",
    "created_at": "2026-11-06T01:20:11+00:00",
    "updated_at": "2026-11-06T01:20:11+00:00",
    "attachments": [
      { "id": 7, "name": "front.jpg", "kind": "image", "content_type": "image/jpeg", "size": 248113,
        "url": "http://localhost/api/v1/files/7/", "uploaded_at": "…", "uploaded_by": "Juan Dela Cruz" }
    ],
    "url": "http://localhost/api/v1/entries/42/"
  }
}

POST /entries/

Adds an entry. Send JSON — or, to include attachments in the same call, multipart/form-data with the JSON in a field named data and the files in fields named files. Returns 201 with the new entry. The fields depend on the form: see Adding & editing data and Form fields.

GET PATCH PUT DELETE /entries/{id}/

GET returns one entry. PATCH (or PUT) changes it: send only the fields to change; details is merged column by column, so {"details": {"noe": 12}} changes just that column. Add "remove_files": [7, 8] to drop attachments. An entry can't move to another form. DELETE removes the entry from the map and deletes its attachments; its history is kept.

POST /entries/{id}/files/

Adds attachments to an existing entry: multipart/form-data with one or more files fields. Only forms that take attachments (Parcel/Lot) accept them. Returns the entry with its attachments.

GET /entries/{id}/history/

Every add, edit and delete of the entry, newest first: action, timestamp, by, via_api_key (the key's name, when made through the API), changes (for edits: label, from, to) and data (the entry as it stood after that change). Also works for deleted entries.

GET DELETE /files/{id}/

GET downloads an attachment with its own content type (images and videos inline, PDF and CSV as attachment); Range requests are supported for video. DELETE removes it from its entry.

Accepted uploads: JPG, PNG, GIF, WebP, MP4, MOV, WebM, PDF and CSV, up to 20 MB each. The platform also has a total storage limit; a save that would exceed it is refused with a message in fields.files.

GET /barangays/

Each barangay with its number of registered businesses (one per business account, from the BPLO listing).

GET /barangays/{number}/businesses/   GET /businesses/

The businesses of one barangay, or of all of them: acctno, name, owner, description, barangay, barangay_number. Filter with q (account no., name, owner or description) and, on /businesses/, barangay (a number). Read only.

GET /zones/

Zones 1–18 with their parcel counts, how many parcels have a PIN, how many are unsure parcels that can be filled in (unsure_parcels), and the count per class (Residential, Commercial, Unsure).

GET /zones/{zone}/parcels/

A zone's parcels as GeoJSON polygons with their tax declaration properties (PIN, ARP/TDN, Class, AREA, PROPERTY_OWNER, LOCATION, …). {zone} is 9 or zone_9. Filters: q (PIN, ARP/TD No., owner or location), class, has_pin=1; geometry=0 leaves the shapes out for a much smaller response. Every parcel carries a parcel_ref: a hash of its shape in the zone file, which stays the same unless the zone file is redrawn. It is how an unsure parcel (no PIN, class Unsure; find them with class=Unsure) and a parcel whose shape is being updated are named. A parcel whose shape was updated is served with its new shape and the entry that holds it in shape_entry.

GET /zones/{zone}/parcels/{pin}/

One parcel with its shape, and under entry the Parcel/Lot entry for it (with attachments), or null.

Adding & editing data

A business — placed by coordinates; its BPLO columns go in details:

curl -X POST http://localhost/api/v1/entries/ \
  -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "category": "business",
        "name": "ABACUS BOOK & CARD CORPORATION",
        "latitude": 14.5376,
        "longitude": 120.9912,
        "details": {
          "acctno": "02-000028",
          "owner": "ABACUS BOOK & CARD CORPORATION",
          "owndesc": "CORPORATION",
          "statcode": "R",
          "foryear": 2025,
          "commaddr": "UNIT 115 G/F, METROPOINT MALL, TAFT AVE., Barangay 097",
          "barangay": "Barangay 97",
          "busdesc": "Retailer - General Merchandise"
        }
      }'

A parcel with photos and a PDF — placed by its zone and PIN (no coordinates; the platform puts it inside the parcel), files in the same call:

curl -X POST http://localhost/api/v1/entries/ \
  -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
  -F 'data={"category": "parcel", "zone": "zone_9", "pin": "123-01-086-03-010",
            "details": {"arp_tdn": "C1-086-00173", "parcel_class": "Residential"}}' \
  -F files=@front.jpg \
  -F files=@tax-declaration.pdf

An unsure parcel — it has no PIN, so give its parcel_ref from GET /zones/{zone}/parcels/?class=Unsure. Only the class is required; send whatever else is known:

curl -X POST http://localhost/api/v1/entries/ \
  -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category": "parcel", "zone": "zone_10", "parcel_ref": "1tiu9qd7px7",
       "details": {"parcel_class": "Commercial", "actual_use": "C"}}'

The entry's values then show on the zone layer in place of "Unsure".

Each parcel has one live Parcel/Lot entry; adding a second for the same PIN (or parcel_ref) fails with a message naming the existing entry — find it with GET /entries/?category=parcel&pin=… (or &parcel_ref=…) and edit that one.

A parcel's updated shape — any parcel, by its parcel_ref. The shape is a GeoJSON Polygon or MultiPolygon in WGS84 longitude / latitude (reproject a shapefile first); it replaces the parcel's shape on the zone layer until the entry is deleted. One live shape update per parcel.

curl -X POST http://localhost/api/v1/entries/ \
  -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category": "parcel_shape", "zone": "zone_10", "parcel_ref": "1tiu9qd7px7",
       "shape": {"type": "Polygon", "coordinates": [[[120.9897, 14.5384], [120.9899, 14.5384],
                                                     [120.9899, 14.5386], [120.9897, 14.5386], [120.9897, 14.5384]]]},
       "details": {"change_type": "Resurvey", "remarks": "New survey plan"}}'

Edit — only what changes:

curl -X PATCH http://localhost/api/v1/entries/42/ \
  -H "Authorization: Bearer $PASAY_GIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"details": {"noe": 12}, "remove_files": [7]}'

Python — add attachments to an existing entry, then delete one:

with open("site-visit.mp4", "rb") as video:
    r = session.post(f"{API}/entries/42/files/", files=[("files", video)])
r.raise_for_status()
attachments = r.json()["properties"]["attachments"]

session.delete(f"{API}/files/{attachments[0]['id']}/").raise_for_status()

Form fields

What each form needs when you add an entry. details fields go inside the details object. Choice fields take one of the listed values exactly. Also available as JSON from /forms/.

Public Facilities "category": "public_facility"

Markets, parks, waiting sheds, sports complexes and community centers — their location and current condition.

FieldRequiredValue
nameyesName of Public Facility
latitudeyesDecimal degrees, about 14.48 to 14.58
longitudeyesDecimal degrees, about 120.95 to 121.05
asset_typeyesFacility Type: one of Market, Park, Waiting Shed, Sports Complex, Community Center, Others
asset_type_other—The type in words, when asset_type is "Others"
conditionyesPublic Facility Condition: one of Operational, Damaged, Under Repair
Power and Utility Infrastructure "category": "utility"

Power lines, transformers, streetlights and telecom towers — their location and operational condition.

FieldRequiredValue
nameyesName of Infrastructure
latitudeyesDecimal degrees, about 14.48 to 14.58
longitudeyesDecimal degrees, about 120.95 to 121.05
asset_typeyesInfrastructure Type: one of Power Line, Transformer, Streetlight, Telecom Tower, Others
asset_type_other—The type in words, when asset_type is "Others"
conditionyesOperational Condition: one of Operational, Non-Operational, Damaged, Under Repair
Overpass "category": "overpass"

Pedestrian, roadway and railway overpasses — their location and structural condition.

FieldRequiredValue
nameyesName of Overpass
latitudeyesDecimal degrees, about 14.48 to 14.58
longitudeyesDecimal degrees, about 120.95 to 121.05
asset_typeyesOverpass Type: one of Pedestrian, Roadway, Railway
asset_type_other—The type in words, when asset_type is "Others"
conditionyesStructural Condition: one of Good, Fair, Poor, Under Repair
Bridge "category": "bridge"

Concrete, steel and composite bridges — their location and physical condition.

FieldRequiredValue
nameyesName of Bridge
latitudeyesDecimal degrees, about 14.48 to 14.58
longitudeyesDecimal degrees, about 120.95 to 121.05
asset_typeyesConstruction Type: one of Concrete, Steel, Composite, Others
asset_type_other—The type in words, when asset_type is "Others"
conditionyesPhysical Condition: one of Good, Fair, Poor, Under Repair
Business "category": "business"

Registered business establishments — the BPLO payment listing columns (account, owner, address, classification, receipts) and exact coordinates.

FieldRequiredValue
nameyesBusiness Name
latitudeyesDecimal degrees, about 14.48 to 14.58
longitudeyesDecimal degrees, about 120.95 to 121.05
details.acctno yes Account No. · text · source column acctno
details.owner yes Owner · text · source column owner
details.owndesc yes Ownership Type · choice · source column owndesc
SINGLECORPORATIONPARTNERSHIPINCORPORATIONCOOPERATIVEFOUNDATIONGOVERNMENT
details.statcode yes Application Status · choice · source column statcode
RN
details.noe — Number of Employees · int · source column noe
details.foryear yes Year Covered · int · source column foryear
details.commaddr yes Business Address · text · source column commaddr
details.barangay yes Barangay · choice · source column brgydesc
Barangay 1Barangay 2Barangay 3Barangay 4Barangay 5Barangay 6Barangay 7Barangay 8Barangay 9Barangay 10Barangay 11Barangay 12… 201 values ("Barangay 1" to "Barangay 201")
details.distdesc — District · choice · source column distdesc
District 01District 02District 03District 04District 05District 06District 07
details.buscode — Business Code · text · source column buscode
details.busdesc yes Business Description · text · source column busdesc
details.catcode — Category Code · text · source column catcode
details.catdesc — Category Description · text · source column catdesc
details.line_stat — Line Status · choice · source column Line Stat
RNC
details.product — Product · text · source column Product
details.area — Area (sq m) · decimal · source column area
details.grossrec — Gross Receipts · decimal · source column grossrec
details.capital — Capital · decimal · source column capital
details.orno — Official Receipt No. · text · source column orno
details.datepaid — Date Paid · date · source column datepaid
details.user_id — Processed By (User ID) · text · source column user_id
details.stamt — Line Amount · decimal · source column stamt
details.tamt — Total Amount Paid · decimal · source column tamt
details.brgyclr — Barangay Clearance · decimal · source column BRGYCLR
details.bqtrind — Business Quarter Indicator · choice · source column bqtrind
01234
details.gqtrind — Gross Quarter Indicator · choice · source column gqtrind
01234
Parcel/Lot "category": "parcel" takes attachments

Pick a parcel by its PIN from a zone layer, or click a parcel with no data (red) to fill it in. Check its tax declaration details (ARP/TDN, class, area, owner, location) and attach photos, videos, PDFs or CSVs.

FieldRequiredValue
zoneyesZone of the parcel, e.g. "zone_9"
pinyesThe parcel's PIN in that zone (or give parcel_ref)
parcel_ref—For an unsure parcel (no PIN): its parcel_ref from the zone parcels list. Then only parcel_class is required
details.arp_tdn yes ARP / Tax Declaration No. · text · source column ARP/TDN
details.parcel_class yes Class · choice · source column Class
ResidentialCommercialUnsure
details.kind — Kind · text · source column KIND
details.sub_class — Sub-class · text · source column SUB_CLASS
details.actual_use — Actual Use · text · source column ACTUAL_USE
details.area — Area (sq m) · decimal · source column AREA
details.location — Location of Property · text · source column LOCATION
details.property_owner — Property Owner · text · source column PROPERTY_OWNER
details.owner_address — Owner Address · text · source column ADDRESS_OF_PROPERTY_OWNER
Parcel Lot Shape "category": "parcel_shape"

Select a parcel on a zone layer and replace its shape with the updated one from your shapefile (.zip, or the .shp with its .shx, .dbf and .prj) or GeoJSON.

FieldRequiredValue
zoneyesZone of the parcel, e.g. "zone_9"
parcel_refyesThe parcel's parcel_ref from the zone parcels list (any parcel)
shapeyesThe new shape: a GeoJSON Polygon or MultiPolygon in WGS84 longitude / latitude
details.change_type yes Type of Change · choice
Boundary correctionResurveyGeoreferencing fixOthers
details.survey_ref — Survey / Plan No. · text
details.remarks yes Reason / Remarks · text
details.source_file — Source File · text
Field Assessment "category": "field_assessment" takes attachments

On site: find the parcel from your location, redraw its shape by tapping its corners on the map or walking them with GPS, and add photos of what you found.

FieldRequiredValue
zoneyesZone of the parcel, e.g. "zone_9"
parcel_refyesThe parcel's parcel_ref from the zone parcels list (any parcel)
shapeyesThe new shape: a GeoJSON Polygon or MultiPolygon in WGS84 longitude / latitude
details.assessed_on yes Date Assessed · date
details.assessor yes Assessor / Team · text
details.change_type yes Type of Change · choice
Boundary correctionResurveyGeoreferencing fixOthers
details.observed_use — Observed Use · choice
ResidentialCommercialMixed useIndustrialInstitutionalVacant lotOthers
details.remarks yes Findings / Remarks · text
details.capture_method — How the Shape Was Captured · choice
Tapped on the mapGPS cornersGPS and map
details.gps_accuracy_m — GPS Accuracy (m) · decimal

Numbers: int and decimal fields take numbers (or numeric strings), 0 or more. Dates are YYYY-MM-DD.

Security checklist

  • Keep keys on servers. Don't put a key in a web page or mobile app — anyone can read it there. Call the API from your back end.
  • Use HTTPS so keys and data are encrypted in transit.
  • One key per system, with the least access it needs (read only unless it must write), and an expiry where it makes sense.
  • Store keys like passwords — environment variables or a secrets manager, never in source code.
  • Revoke a key straight away if it may have leaked, and issue a new one.
  • Attachments can contain personal or property documents — protect what you download.