> For the complete documentation index, see [llms.txt](https://developers.felt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.felt.com/rest-api/refreshing-live-data-layers.md).

# Refreshing live data layers

It's common to have data update on a regular basis, such as every week or every month. Instead of having to re-upload and style the new data, it can be very convenient to simply refresh a layer using a new data source.

{% hint style="warning" %}
A layer must have finished uploading successfully (`status` of `completed`) before it can be refreshed — see [Monitoring progress](/rest-api/uploading-files-and-urls.md#monitoring-progress). Refreshing a layer that is still in its initial processing returns `404` with the detail "Layer still in initial processing".
{% endhint %}

## Refreshing a layer with a file

{% hint style="info" %}
Refreshing a file layer is a single function call using the `felt-python` library.
{% endhint %}

Just like [regular file uploads](/rest-api/uploading-files-and-urls.md), refreshing a layer with a new file is a two-step process:

### 1. Request a refresh via the Felt API

Perform a `POST` request to receive an S3 presigned URL which you can later upload your files to:

{% tabs %}
{% tab title="Python" %}

```python
import requests

# Your API token should look like this:
# api_token = "felt_pat_ABCDEFUDQPAGGNBmX40YNhkCRvvLI3f8/BCwD/g8"
api_token = "<YOUR_API_TOKEN>"
map_id = "<YOUR_MAP_ID>"
layer_id = "<YOUR_LAYER_ID>"

r = requests.post(
  f"https://felt.com/api/v2/maps/{map_id}/layers/{layer_id}/refresh",
  headers={"Authorization": f"Bearer {api_token}"}
)
assert r.ok
presigned_upload = r.json()
```

{% endtab %}

{% tab title="felt-python" %}

```python
import os

from felt_python import refresh_file_layer

# Setting your API token as an env variable can save
# you from repeating it in every function call
os.environ["FELT_API_TOKEN"] = "<YOUR_API_TOKEN>"

map_id = "<YOUR_MAP_ID>"
layer_id = "<YOUR_LAYER_ID>"
new_file_name = "<PATH_TO_NEW_FILE>"

refresh_file_layer(
    map_id=map_id,
    layer_id=layer_id,
    file_name=new_file_name
)
```

{% endtab %}
{% endtabs %}

### 2. Upload your file(s) to Amazon S3

{% tabs %}
{% tab title="Python" %}

```python
# This code is a continuation of the previous Python code block
# and assumes you already have a "presigned_upload" variable

file_name = "<YOUR_FILE_WITH_EXTENSION>"  # Example: regions.geojson

url = presigned_upload["url"]
presigned_attributes = presigned_upload["presigned_attributes"]
# A 204 response indicates that the upload was successful
with open(file_name, "rb") as file_obj:
    output = requests.post(
        url,
        # Order is important, file should come at the end
        files={**presigned_attributes, "file": file_obj},
    )
```

{% endtab %}

{% tab title="felt-python" %}

```python
# Nothing! Refreshing a file layer is a single step with the felt-python library
```

{% endtab %}
{% endtabs %}

## Refreshing a layer with a URL

Similar to [a URL upload](/rest-api/uploading-files-and-urls.md), refreshing an existing URL layer is just a matter of making a single `POST` request. The refresh re-fetches the layer's existing import URL — the request takes no body, so the URL itself cannot be changed during a refresh:

{% tabs %}
{% tab title="curl" %}

```bash
# Your API token and map ID should look like this:
# FELT_API_TOKEN="felt_pat_ABCDEFUDQPAGGNBmX40YNhkCRvvLI3f8/BCwD/g8"
FELT_API_TOKEN="<YOUR_API_TOKEN>"
MAP_ID="<YOUR_MAP_ID>"
LAYER_ID="<YOUR_LAYER_ID>"

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${FELT_API_TOKEN}" \
  "https://felt.com/api/v2/maps/${MAP_ID}/layers/${LAYER_ID}/refresh"
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

# Your API token and map ID should look like this:
# api_token = "felt_pat_ABCDEFUDQPAGGNBmX40YNhkCRvvLI3f8/BCwD/g8"
api_token = "<YOUR_API_TOKEN>"
map_id = "<YOUR_MAP_ID>"
layer_id = "<YOUR_LAYER_ID>"

r = requests.post(
  f"https://felt.com/api/v2/maps/{map_id}/layers/{layer_id}/refresh",
  headers={"Authorization": f"Bearer {api_token}"}
)
assert r.ok
print(r.json())
```

{% endtab %}

{% tab title="felt-python" %}

```python
from felt_python import refresh_url_layer

refresh_url_layer(map_id, layer_id)
```

{% endtab %}
{% endtabs %}

## Scheduled refresh

Layers imported from a URL or a connected data source can refresh on a schedule, so you don't have to call the refresh endpoint yourself. Set `refresh_period` with `POST /api/v2/maps/{map_id}/layers`, which takes a list of layer updates:

{% tabs %}
{% tab title="curl" %}

```bash
curl -X POST \
  -H "Authorization: Bearer ${FELT_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '[{"id": "'"${LAYER_ID}"'", "refresh_period": "hour"}]' \
  "https://felt.com/api/v2/maps/${MAP_ID}/layers"
```

{% endtab %}

{% tab title="Python" %}

```python
import os
import requests

r = requests.post(
    f"https://felt.com/api/v2/maps/{map_id}/layers",
    headers={"Authorization": f"Bearer {os.environ['FELT_API_TOKEN']}"},
    json=[{"id": layer_id, "refresh_period": "hour"}],
)
r.raise_for_status()
```

{% endtab %}
{% endtabs %}

`refresh_period` takes one of `15 min`, `30 min`, `hour`, `3 hours`, `6 hours`, `12 hours`, `day`, `week`, `month` or `disabled`. Because the body is a list, you can set the schedule for many layers on a map in one call.

The fastest schedule depends on your plan:

| Plan       | Fastest schedule |
| ---------- | ---------------- |
| Team       | `day`            |
| Business   | `hour`           |
| Enterprise | `15 min`         |

Asking for a faster schedule than your plan allows returns `422`. Each refresh counts the full dataset toward your monthly processing allowance, so choose the slowest schedule that meets your needs; see [Plans, access and limits](/plans-access-and-limits.md#monthly-limits).

The layer's response (`GET /api/v2/maps/{map_id}/layers/{layer_id}`) shows the schedule's state:

* `refresh_status` is `active`, `paused` or `disabled`. Felt pauses a schedule after repeated failures and sets `paused_reason` to `consecutive_failures`.
* `next_refresh_at` is when the next refresh will run. If a downgrade left `refresh_period` faster than the plan allows, the layer refreshes at the plan's fastest rate and `next_refresh_at` reflects that.
* `last_refreshed_at` is when the layer's data last changed, from a scheduled or manual refresh or a direct edit.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developers.felt.com/rest-api/refreshing-live-data-layers.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
