> For the complete documentation index, see [llms.txt](https://docs.graniot.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graniot.com/reference/api-reference/parcels.md).

# Parcels

Parcels represent individual agricultural plots or fields within a farm. Each parcel has a geometry, a name, and can include custom metadata for filtering and organization.

## Creating Parcels in Bulk

`POST /api/parcels/` creates parcels **in batches**. The `parcels` field is an array that accepts up to **50 parcels per request** — you do not need to call the endpoint once per parcel.

```json
{
  "farm": { "id": 1234 },
  "parcels": [
    { "name": "Field A", "metadata": { "Supplier ID": "SUP-0001" }, "geom": { "...": "" } },
    { "name": "Field B", "metadata": { "Supplier ID": "SUP-0002" }, "geom": { "...": "" } }
  ]
}
```

A successful batch returns **`201 Created`** with one GeoJSON Feature per created parcel, in the order they were sent. **This response is the only place the Graniot identifiers are returned — persist them**: `id` at the top level of each Feature, and the parcel UUID at `properties.key`.

{% hint style="warning" %}
Success is **`201`**. A `200 OK` response with a body like `{"message": "Missing Farm ID"}` means the request was **not** processed and nothing was created — check the status code, not just the status class.
{% endhint %}

### Batch behaviour

* **Each request is atomic.** If any parcel in the batch is invalid, the whole request is rejected with `400` and **none** of its parcels are created.
* **Requests are not idempotent.** Sending the same batch twice creates the parcels twice. Since billing is based on total hectares, duplicates are billable — verify before resending.
* **Maximum parcel size is 100 hectares**, unless a different limit was agreed for your account.
* **A `504 Gateway Time-out` does not necessarily mean the batch failed.** If a request exceeds the gateway's response limit, the server may still commit the batch. Verify with `GET /api/farms/{id}/parcels/` before resending, and reduce your batch size.

For a complete walkthrough — validation, error recovery, resuming an interrupted upload and enabling imagery — see [Bulk Parcel Onboarding](/guides/bulk-parcel-onboarding.md).

### Geometry formats

Parcels accept two geometry types.

**Polygon** — use when you have the real field boundary. The area is computed from the geometry:

```json
{
  "name": "Field A",
  "geom": {
    "type": "Feature",
    "properties": {},
    "geometry": {
      "type": "Polygon",
      "coordinates": [[[-1.101, 38.139], [-1.100, 38.139], [-1.100, 38.138], [-1.101, 38.138], [-1.101, 38.139]]]
    }
  }
}
```

**Point** — use when you only know the location and approximate size of a plot. The platform generates a square parcel of the given area, centred on the point:

```json
{
  "name": "Smallholder plot SUP-0001",
  "geom": {
    "type": "Feature",
    "properties": { "metadata": { "hectares": 0.4 } },
    "geometry": { "type": "Point", "coordinates": [30.0, 0.1] }
  }
}
```

{% hint style="info" %}
`properties.metadata.hectares` is **required** for point geometries — a point without it rejects the whole batch. Coordinates are always in GeoJSON order: `[longitude, latitude]` (WGS84).
{% endhint %}

## Parcel Metadata

The `metadata` field allows you to attach custom key-value data to any parcel. This is useful for storing information like supplier IDs, regions, crop types, or any other custom attributes your system needs.

### Creating Parcels with Metadata

When creating parcels via the API, include a `metadata` object:

```json
{
  "farm": {"id": 123},
  "parcels": [
    {
      "name": "Parcel A",
      "metadata": {
        "Supplier ID": "SUP-0001",
        "region": "North",
        "contract_year": "2024"
      },
      "geom": { "...": "" }
    },
    {
      "name": "Parcel B",
      "metadata": {
        "Supplier ID": "SUP-0002",
        "region": "North",
        "contract_year": "2024"
      },
      "geom": { "...": "" }
    }
  ]
}
```

### Updating Parcel Metadata

Use the PATCH endpoint to update metadata:

```
PATCH /api/parcels/{parcel_id}/
```

```json
{
  "id": 12345,
  "metadata": {
    "Supplier ID": "SUP-0001",
    "region": "North",
    "status": "active"
  }
}
```

### Viewing Metadata in Embedded Maps

Metadata is visible in the embedded map in two ways:

1. **Parcel tooltips**: Users can toggle which metadata fields appear when hovering over a parcel
2. **Parcel detail panel**: All metadata key-value pairs are displayed when opening a parcel's details

### Filtering by Metadata in Embedded Maps

You can filter the embedded map to show only parcels with specific metadata values using URL parameters:

```
https://embed.graniot.com/?auth_id=YOUR_TOKEN&metadata[Supplier+ID]=SUP-0001
```

See the [Embedded Maps](/embedded-maps.md) documentation for more filtering options.

***

{% openapi src="<https://app.graniot.com/api/doc/>" path="/api/parcels/" method="get" %}
<https://app.graniot.com/api/doc/>
{% endopenapi %}

{% openapi src="<https://app.graniot.com/api/doc/>" path="/api/parcels/" method="post" %}
<https://app.graniot.com/api/doc/>
{% endopenapi %}

{% openapi src="<https://app.graniot.com/api/doc/>" path="/api/parcels/{id}/" method="get" %}
<https://app.graniot.com/api/doc/>
{% endopenapi %}

{% openapi src="<https://app.graniot.com/api/doc/>" path="/api/parcels/{id}/" method="delete" %}
<https://app.graniot.com/api/doc/>
{% endopenapi %}

{% openapi src="<https://app.graniot.com/api/doc/>" path="/api/parcels/{id}/" method="patch" %}
<https://app.graniot.com/api/doc/>
{% endopenapi %}
