---
title: "Quick start: Tinybird CLI"
meta:
    description: Follow this step-by-step tutorial to get started with Tinybird.
---

# Quick start: Tinybird CLI

Follow these steps to install Tinybird Local and Tinybird CLI on your machine, build your first data project, and deploy it to Tinybird Cloud.

See [Core concepts](/forward/get-started/concepts) for a complete overview of Tinybird.

{% callout type="note" %}
This quickstart is based on the Tinybird CLI and datafiles. If you're looking for a different approach, see [Quick starts](/forward/get-started/quickstarts).
{% /callout %}

## Before you begin

To get started, you need the following:

- A Tinybird account. You can create one at [cloud.tinybird.co](https://cloud.tinybird.co).
- Git.
- [Optional for local development] A container runtime, like Docker or Orbstack. If you don't have it, select `branch` as development mode when running `tb init`.

## Deploy a new project in five minutes

{% steps %}

### Install Tinybird CLI

Run the following command to install the Tinybird CLI:

{% snippet title="install-tinybird-forward" /%}

{% callout type="warning" %}
**Already have Tinybird Classic CLI installed?** Both Classic and Forward CLIs use the `tb` command. Run `which tb` to check if you already have a `tb` binary installed. If you do, see [Managing CLI versions](/forward/install-tinybird/migrate#install-the-tinybird-forward-cli) to avoid conflicts.
{% /callout %}

### Optional: run Tinybird Local

In another terminal tab, run `tb local start` to start a Tinybird Local instance in a Docker container, allowing you to develop and test your project locally.

```shell
tb local start
```

You can skip this step selecting `branch` as development mode.

### Create a project

Start by initializing a new project:

```shell
tb init
```

This creates the standard project folders (`datasources/`, `pipes/`, `endpoints/`, `fixtures/`, `tests/`, and others), plus other useful stuff like CI/CD templates or Agent Skills.

Initialize git and check out a new branch

```shell
git init && git checkout -b tinybird_intro
```

First things first, you need a data source. Use NYC yellow taxis [dataset](https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page) if you don't have sample data.

{% tabs initial="Tinybird Branches" %}

{% tab label="Tinybird Branches" %}

```shell
tb --cloud datasource create --url https://tbrd.co/taxi_data.parquet --name trips
```

{% /tab %}

{% tab label="Tinybird Local" %}

```shell
tb datasource create --url https://tbrd.co/taxi_data.parquet --name trips
```

{% /tab %}

{% /tabs %}

This creates a new file in your project:

```txt
» Creating .datasource file...
/datasources/trips.datasource
✓ .datasource created!
```

Data sources are the definition of the database tables where you will store the data. More information about data sources [here](/forward/dev-reference/datafiles/datasource-files).

{% callout type="warning" %}
**Always run `tb` from your Tinybird project root**: When you run `tb` commands, the CLI loads environment variables from `.env` files in the current directory and its parent directories. If you run `tb` from a parent directory (for example, running `tb` from `./my-app/` when your Tinybird project is in `./my-app/tinybird/`), the CLI may load unintended `.env` files from your application's root. Always execute `tb` commands from the root of your Tinybird project to ensure the correct environment variables are loaded.
{% /callout %}

#### Content of /datasources/trips.datasource

Inspecting the file you see a description and the schema, with the column names, their types, and the [JSONPath](/forward/dev-reference/datafiles/datasource-files#jsonpath-expressions) to access the parquet fields.

```tb {% title="datasources/trips.datasource" %}
DESCRIPTION >
    Generated from https://tbrd.co/taxi_data.parquet

SCHEMA >
    VendorID Nullable(Int32) `json:$.VendorID`,
    tpep_pickup_datetime Nullable(DateTime64(6)) `json:$.tpep_pickup_datetime`,
    tpep_dropoff_datetime Nullable(DateTime64(6)) `json:$.tpep_dropoff_datetime`,
    passenger_count Nullable(Int64) `json:$.passenger_count`,
    trip_distance Nullable(Float64) `json:$.trip_distance`,
    RatecodeID Nullable(Int64) `json:$.RatecodeID`,
    store_and_fwd_flag Nullable(String) `json:$.store_and_fwd_flag`,
    PULocationID Nullable(Int32) `json:$.PULocationID`,
    DOLocationID Nullable(Int32) `json:$.DOLocationID`,
    payment_type Nullable(Int64) `json:$.payment_type`,
    fare_amount Nullable(Float64) `json:$.fare_amount`,
    extra Nullable(Float64) `json:$.extra`,
    mta_tax Nullable(Float64) `json:$.mta_tax`,
    tip_amount Nullable(Float64) `json:$.tip_amount`,
    tolls_amount Nullable(Float64) `json:$.tolls_amount`,
    improvement_surcharge Nullable(Float64) `json:$.improvement_surcharge`,
    total_amount Nullable(Float64) `json:$.total_amount`,
    congestion_surcharge Nullable(Float64) `json:$.congestion_surcharge`,
    Airport_fee Nullable(Float64) `json:$.Airport_fee`,
    cbd_congestion_fee Nullable(Float64) `json:$.cbd_congestion_fee`

ENGINE "MergeTree"
# ENGINE_SORTING_KEY "user_id, timestamp"
# ENGINE_TTL "timestamp + toIntervalDay(60)"
# 📖 See the datafile reference at https://www.tinybird.co/docs/forward/dev-reference/datafiles/datasource-files
```

### Add the lookup table and create an endpoint

Projects in Tinybird usually consist of data sources and API Endpoints to expose the query results. Create one to check in which zone passengers give bigger tips.
Also, trips data source has two columns, `PULocationID` and `DOLocationID` that need a reference table to be understood. Add that table as well.

Create the lookup data source:

{% tabs initial="Tinybird Branches" %}

{% tab label="Tinybird Branches" %}

```shell
tb --cloud datasource create --url "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv" --name taxi_zone_lookup
```

{% /tab %}

{% tab label="Tinybird Local" %}

```shell
tb datasource create --url "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv" --name taxi_zone_lookup
```

{% /tab %}

{% /tabs %}

This creates `datasources/taxi_zone_lookup.datasource`.

#### /datasources/taxi_zone_lookup.datasource

In this case, as it is a CSV data source there are no JSONPaths and column names are taken from CSV headers.

```tb {% title="datasources/taxi_zone_lookup.datasource" %}
DESCRIPTION >
    Generated from https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv

SCHEMA >
    `locationid` Int32,
    `borough` String,
    `zone` String,
    `service_zone` String

ENGINE "MergeTree"
# ENGINE_SORTING_KEY "user_id, timestamp"
# ENGINE_TTL "timestamp + toIntervalDay(60)"
# 📖 See the datafile reference at https://www.tinybird.co/docs/forward/dev-reference/datafiles/datasource-files
```

Then create a new file `endpoints/best_tip_zones.pipe` with the code below.

#### endpoints/best_tip_zones.pipe

Endpoints are a kind of [pipe](/forward/work-with-data/pipes) that you can call from other applications. You have data in a data source, use a pipe to build SQL logic, and then publish the result of your query as a REST API endpoint. Pipes contain just SQL and a [templating language](/forward/work-with-data/query-parameters) that lets you add query parameters to the API. More details about Endpoints [here](/forward/work-with-data/publish-data/endpoints).

```sql {% title="endpoints/best_tip_zones.pipe" %}
TOKEN best_tips_read READ

DESCRIPTION >
    Endpoint that finds the zone most likely to have better tips for a given pickup location

NODE pickup_zone_lookup
SQL >
    %
    SELECT locationid
    FROM taxi_zone_lookup
    WHERE zone ILIKE concat('%', {{String(pickup_zone, 'JFK Airport')}}, '%')
    LIMIT 1

NODE tip_analysis
SQL >
    %
    SELECT
        t.DOLocationID,
        tz.zone as destination_zone,
        avg(t.tip_amount) as avg_tip,
        count(*) as trip_count,
        avg(t.tip_amount / nullif(t.total_amount, 0)) as tip_percentage
    FROM trips t
    LEFT JOIN taxi_zone_lookup tz ON t.DOLocationID = tz.locationid
    WHERE t.PULocationID = (SELECT locationid FROM pickup_zone_lookup)
        AND t.tip_amount > 0
        AND t.total_amount > 0
    GROUP BY t.DOLocationID, tz.zone
    HAVING trip_count >= 10
    ORDER BY avg_tip DESC
    LIMIT 10

TYPE endpoint
```

Note you just defined a token with read permissions for this Endpoint. You will use it later.

### Build the project

```shell
tb build
```

Depending on your configuration in `tinybird.config.json`, builds will happen against Tinybird Local or against a branch (detected from your git branch). This step builds the resources definitions, `/datasources/trips.datasource`, `datasources/taxi_zone_lookup.datasource`, and `endpoints/best_tip_zones.pipe` in actual resources in a Tinybird development environment.

### Load sample data for local testing

Append sample data so you can query the endpoint in the dev environment:
{% tabs initial="Tinybird Branches" %}

{% tab label="Tinybird Branches" %}

```shell
tb --branch=tinybird_intro datasource append trips --url "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2025-05.parquet"
tb --branch=tinybird_intro datasource append taxi_zone_lookup --url "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv"
```

{% /tab %}

{% tab label="Tinybird Local" %}

```shell
tb datasource append trips --url "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2025-05.parquet"
tb datasource append taxi_zone_lookup --url "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv"
```

{% /tab %}

{% /tabs %}

### Test the API Endpoint

The goal is to have a working endpoint that you can call from other applications.

#### About tokens

All Tinybird API calls require authentication using a **token**. Tokens control who can access your data and what they can do with it. Think of them like API keys that protect your endpoints.

For local development, Tinybird automatically creates an admin token for you: `admin local_testing@tinybird.co`. This token has full permissions in your local environment, making it perfect for testing. For Branches, new tokens matching the existing oned in the Tinybird Cloud Workspace are created. For this quickstart is OK to use admin, but define more scoped ones for production.

#### Making your first API call

Check your `HOST` and `TOKEN` for the environment you are developing in:

- Local: Default local url is `http://localhost:7181` and your token is `admin local_testing@tinybird.co`. Copy the token value with `tb token copy`.
- Branches: host is the same as Tinybird Cloud, you can check it in the `.tinyb` file. Same for token, but pointing to the branch with `tb --branch=tinybird_intro token copy`.

{% tabs initial="Tinybird Branches" %}

{% tab label="Tinybird Branches" %}

```shell
cat .tinyb | jq -r ".host" | pbcopy && TB_BRANCH_HOST=$(pbpaste)
tb --branch=tinybird_intro token copy "best_tips_read" && TB_BRANCH_TOKEN=$(pbpaste)

curl -X GET "$TB_BRANCH_HOST/v0/pipes/best_tip_zones.json?token=$TB_BRANCH_TOKEN&pickup_zone=Newark+Airport"
```

{% /tab %}

{% tab label="Tinybird Local" %}

```shell
tb token copy "best_tips_read" && TB_LOCAL_TOKEN=$(pbpaste)

curl -X GET "http://localhost:7181/v0/pipes/best_tip_zones.json?token=$TB_LOCAL_TOKEN&pickup_zone=Newark+Airport"
```

{% /tab %}

{% /tabs %}

```txt
{
  "meta": [
    { "name": "DOLocationID", "type": "Nullable(Int32)" },
    { "name": "destination_zone", "type": "Nullable(String)" },
    { "name": "avg_tip", "type": "Float64" },
    { "name": "trip_count", "type": "UInt64" },
    { "name": "tip_percentage", "type": "Float64" }
  ],
  "data": [
    {
      "DOLocationID": 1,
      "destination_zone": "Newark Airport",
      "avg_tip": 6.18,
      "trip_count": 27,
      "tip_percentage": 0.22
    }
  ]
}
```

Output is a JSON with the results and some metadata, but note you can change the extension and get the endpoint result as .csv, .parquet...

### Deploy to Tinybird Cloud

To deploy to your Tinybird Cloud Workspace, create a deployment using the `tb deploy` command. This prepares all the resources in the Cloud environment.

```shell
tb deploy
```

### Append data to Tinybird Cloud

Use `tb datasource append` with the `--cloud` flag to ingest the data from the URL into Tinybird Cloud.

```shell
tb --cloud datasource append trips --url "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2025-05.parquet"
tb --cloud datasource append taxi_zone_lookup --url "https://d37ci6vzurychx.cloudfront.net/misc/taxi_zone_lookup.csv"

# Running against Tinybird Cloud: Workspace quickstart

# » Appending data to trips
# ✓ Rows appended!
# Running against Tinybird Cloud: Workspace quickstart

# » Appending data to taxi_zone_lookup
# ✓ Rows appended!
```

### Open the project in Tinybird Cloud

To open the project in Tinybird Cloud, run the following command:

```shell
tb --cloud open
```

Go to **Endpoints** and select an endpoint to see stats and snippets.

{% callout type="note" %}
**Tokens in production**: Your local admin token (`admin local_testing@tinybird.co`) won't work with Tinybird Cloud. The cloud environment has its own tokens for security. When you need to call your cloud endpoints from applications, create specific tokens with limited permissions. See [Tokens](/forward/administration/tokens) for details.
{% /callout %}

{% /steps %}

## Next steps

- Familiarize yourself with Tinybird concepts. See [Core concepts](/forward/get-started/concepts).
- Learn about datafiles, like .datasource and .pipe files. See [Datafiles](/forward/dev-reference/datafiles).
- Get data into Tinybird from a variety of sources. See [Get data in](/forward/get-data-in).
- Learn about authentication and securing your endpoints. See [Tokens](/forward/administration/tokens).
- Browse the Tinybird CLI commands reference. See [Commands reference](/forward/dev-reference/commands).
- Learn with a more detailed example. See [Learn](/forward/get-started/learn).
- Detect and fix Quarantine errors. See [Quarantine](/forward/get-data-in/quarantine).
