Connect all your APIs & services
without integration code.

Tag your APIs.
Query for data using TaxiQL.
Taxi handles the orchestration.

APIs change. Consumers shouldn't have to.

Traditional API integration requires writing and maintaining complex orchestration code.
With Taxi, you simply describe the data you need, and Taxi automatically discovers how to fetch it.

Without Taxi

What you write today, then rewrite again when any API changes

integration.js
// Without Taxi: The integration code you actually write (and maintain)
import axios from 'axios';

const ORDER_API = process.env.ORDER_API_URL;
const CUSTOMER_API = process.env.CUSTOMER_API_URL;
const SHIPPING_API = process.env.SHIPPING_API_URL;
const PAYMENT_API = process.env.PAYMENT_API_URL;

async function withRetry(fn, retries = 3, delay = 500) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === retries - 1) throw err;
      await new Promise(res => setTimeout(res, delay * Math.pow(2, i)));
    }
  }
}

async function getOrdersWithDetails(customerId) {
  if (!customerId) throw new Error('customerId is required');

  let customer;
  try {
    const res = await withRetry(() =>
      axios.get(`${CUSTOMER_API}/customers/${customerId}`, {
        headers: { Authorization: `Bearer ${getToken()}` },
        timeout: 5000,
      })
    );
    customer = res.data;
  } catch (err) {
    throw new Error(`Failed to fetch customer ${customerId}: ${err.message}`);
  }

  let orders;
  try {
    const res = await withRetry(() =>
      axios.get(`${ORDER_API}/orders`, {
        params: { customerId },
        headers: { Authorization: `Bearer ${getToken()}` },
        timeout: 5000,
      })
    );
    // Note: this API returns { data: { items: [...] } } not an array
    orders = res.data?.data?.items ?? [];
  } catch (err) {
    throw new Error(`Failed to fetch orders for ${customerId}: ${err.message}`);
  }

  if (!Array.isArray(orders) || orders.length === 0) return [];

  const enrichedOrders = await Promise.all(
    orders.map(async (order) => {
      // Shipping and payment APIs use different ID fields — easy to miss
      const trackingId = order.trackingId ?? order.tracking_id;
      const paymentId = order.paymentId ?? order.payment_ref;

      const [shippingResult, paymentResult] = await Promise.allSettled([
        withRetry(() =>
          axios.get(`${SHIPPING_API}/tracking/${trackingId}`, {
            headers: { Authorization: `Bearer ${getToken()}` },
            timeout: 5000,
          })
        ),
        withRetry(() =>
          axios.get(`${PAYMENT_API}/payments/${paymentId}`, {
            headers: { Authorization: `Bearer ${getToken()}` },
            timeout: 5000,
          })
        ),
      ]);

      const shipping =
        shippingResult.status === 'fulfilled'
          ? shippingResult.value.data
          : null;
      const payment =
        paymentResult.status === 'fulfilled'
          ? paymentResult.value.data
          : null;

      // Field names diverged between teams — map them manually
      return {
        orderId: order.id ?? order.order_id,
        total: order.totalAmount ?? order.total,
        customerName: `${customer.firstName} ${customer.lastName}`,
        shippingStatus: shipping?.currentStatus ?? shipping?.status ?? 'unknown',
        paymentMethod: payment?.method ?? payment?.paymentType ?? null,
      };
    })
  );

  return enrichedOrders;
}

With Taxi

Declarative query that adapts automatically as your APIs evolve

Taxi automatically discovers data by joining across services. Click "Show Query Plan" to see how Taxi builds the integration.

Schema
Query Plan
Play with this snippet by editing it here, or edit it on Taxi Playground
Result
Query failed

Describe

BYO API Specs

Taxi works with your existing API schemas and specs - simply embed tags to show how data relates.

Alternatively, use Taxi to describe your APIs, CSV files, Event payloads and more

customer-api.oas.yaml
# An extract of an OpenAPI spec:
components:
  schemas:
    Customer:
      properties:
        id:
          type: string
           # Embed semantic type metadata directly in OpenAPI
           x-taxi-type:
              name: CustomerId
          

Orchestrate & Integrate

You query. Taxi integrates & adapts.

Write queries for data using the same tags you embedded in your API specs. Taxi's query engine handles the integration, linking across APIs, databases, Kafka topics, S3 buckets, the lot.

There's no resolvers or glue code to maintain, API clients to generate, or YAML whitespace headaches.

As your API specs change, Taxi queries automatically adapt.

query.taxi
// Send a query for data to Orbital,
// and it builds the integration on demand,
// using metadata embedded in your API specs
find { Movies(ReleaseYear > 2018)[] }
as {
   // Consumers define the schema they want.
   // Orbital works out where to fetch data from
   title : MovieTitle // .. read from a db
   review : ReviewScore // .. call a REST API to find this
   awards : AwardTitle[] // ... and a gRPC service to find this.
}
        

Frequently asked questions

Got another gnarly question? We'd love to hear it. Come and chat on Slack.