# Bubble

Bubble's **API Connector** sends its calls from Bubble's servers, so a server-only key belongs here rather than a publishable one. The request below was sent to production exactly as the call builds it; the Bubble steps follow Bubble's own manual as of 2026-09-17, and the API Connector is listed on the Free plan.

Before you start, create a key as described in [A key for an automation](/en/integrations/#a-key-for-an-automation), scoped to `chart`.

## 1. Create the collection

In the API Connector plugin press **+ New** and fill the collection in:

- **Collection name**: `AstroWay`. Bubble's manual warns that the collection name ships inside your app's client-side source, so keep it plain.
- **Authentication**: **Private key in header**, with the header name `X-Api-Key` and your key as the value.

If you would rather leave Authentication at **None or self-handled**, add the key under **Shared headers for all calls** with **+ Add a shared header** and tick **Private** on it. Either way the key stays on the server.

<Aside type="caution" title="Never put the key in the Body">
Bubble documents the Body as client safe: its values are sent to the visitor's browser when the call is made. The URL is not, and a header with **Private** ticked is not. A key pasted into the JSON body is a key you have published.
</Aside>

## 2. Add the call

Add a call inside the collection and fill it in:

- **Call name**: `Natal chart`.
- **Use as**: **Action** for a workflow, **Data** if a page should read it directly.
- **Method**: `POST`.
- **URL**:

  ```
  https://api.astroway.info/v1/chart
  ```

- **Body type**: **JSON**.
- **Body**: the JSON below. Angle brackets are Bubble's syntax for a parameter, and each one creates an entry under the body.

```json
{
  "date": "<date>",
  "time": "<time>",
  "timezone": "<timezone>",
  "latitude": "<latitude>",
  "longitude": "<longitude>"
}
```

Under the body, **untick Private** on each of the five parameters so your app can set them, and give them the values below as defaults. Then press **Initialize call**.

| Parameter | Value to initialize with |
|---|---|
| `date` | `1990-05-15` |
| `time` | `14:30:00` |
| `timezone` | `Europe/Kyiv` |
| `latitude` | `50.45` |
| `longitude` | `30.52` |

Initialization is what teaches Bubble the shape of the response, and Bubble's manual asks you to use sample data for it, which is what the five values above are. A good answer carries `ok` true and a `data` object with `planets`, `houses` and `input`.

Re-initialize whenever you add or rename a parameter: the response schema is recorded at initialization.

## 3. Read the answer

After initialization Bubble lists the response fields with a type and an include checkbox. The three most asked-for values:

- **Ascendant**: `data > houses > ascendant`.
- **Sun**: the first entry of `data > planets`, its `longitude`.
- **Moon**: the second entry, its `longitude`.
- **Offset that was used**: `data > input > timezoneOffset`, `4` for the example above, because Kyiv kept UTC+4 on that date.

The sign is the longitude divided by 30, rounded down, used as an index into the twelve names. In an expression: divide `data's houses's ascendant` by 30 and take the whole part, then look the number up in an option set or a list of the twelve signs. For the example the Ascendant is `159.26`, which gives `5`, which is Virgo.

## Errors

Tick **Include errors in response &amp; allow workflow actions to continue** on the call if you want to handle failures in a workflow instead of letting them stop it. Bubble notes that switching this after initialization changes the response format, so re-initialize when you do.

- **`400 INVALID_FIELD` with `timezone` in `details`**: the zone is empty, an abbreviation such as `EST`, or not a zone name.
- **`400` with `date` or `time` in `details`**: the format is not `YYYY-MM-DD` / `HH:MM:SS`. Bubble's `:formatted as` on a date fixes that before the call.
- **`403 ENDPOINT_NOT_IN_SCOPE`**: the key's scope does not include `chart`.
- **`429 KEY_BUDGET_EXHAUSTED`**: the key reached its own budget; raise it in the dashboard.

<Aside type="note">
Bubble sends calls from shared infrastructure, so its source address is shared with other Bubble apps. Our limits are counted per key rather than per address, so that does not eat your quota. The API does not geocode: coordinates come from your own inputs or from a geocoding service you hold a key for.
</Aside>
