Givebear LogoGivebear
Connect API

Quickstart

Create an API key, then make your first authenticated Connect API call in your language of choice.

Go from zero to a live, authenticated request in a few minutes. You create an API key in the dashboard, then read donations and your organization profile over the API.

Every step shows every supported language: curl, the typed @givebear/connect JavaScript client, and plain HTTP in Python, PHP, Ruby, and Go. Pick a language once; the tabs stay in sync across the page and remember your choice. The API reference generates samples for these and more, per endpoint.

This tutorial covers a single organization using an API key. To act on behalf of many organizations, read Authentication for the OAuth flow instead.

Prefer to see it running first? Open the full example (OAuth, the read API, and live webhooks) in StackBlitz:

Before you start

  • You are an owner or admin of a Givebear organization.
  • Any HTTP client works. The snippets use curl, Node.js 18+, Python 3 with requests, PHP with the cURL extension, Ruby's standard library, and Go's standard library.

Create an API key

Open your dashboard and go to Developers -> API keys. Create a key, then select the scopes it needs:

  • donations:read to read donation records.
  • reference:read to read the organization profile, campaigns, and funds.

Givebear shows the key once. Copy it now. It starts with gb_live_.

The key is a secret. Store it in an environment variable or a secrets manager, never in client-side code or version control. See the full scope list.

Export it so the snippets below can read it:

export GIVEBEAR_TOKEN="gb_live_..."

Make your first request

Every endpoint lives under https://givebear.io/api/v1 and takes the key as a bearer token. Confirm the key works by reading the organization it belongs to.

curl https://givebear.io/api/v1/organization \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN"
// npm install @givebear/connect
import { GivebearConnect } from "@givebear/connect";

const gb = new GivebearConnect({ token: process.env.GIVEBEAR_TOKEN! });

const org = await gb.organization.get();
console.log(org.name);
# pip install requests
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['GIVEBEAR_TOKEN']}"}

org = requests.get(
    "https://givebear.io/api/v1/organization", headers=headers
).json()
print(org["name"])
<?php
$token = getenv("GIVEBEAR_TOKEN");

$ch = curl_init("https://givebear.io/api/v1/organization");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
$org = json_decode(curl_exec($ch), true);
curl_close($ch);

echo $org["name"];
require "net/http"
require "json"

uri = URI("https://givebear.io/api/v1/organization")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("GIVEBEAR_TOKEN")}"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
puts JSON.parse(response.body)["name"]
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://givebear.io/api/v1/organization", nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("GIVEBEAR_TOKEN"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var org struct {
		Name string `json:"name"`
	}
	json.NewDecoder(resp.Body).Decode(&org)
	fmt.Println(org.Name)
}
{
  "id": "...",
  "object": "organization",
  "name": "Community Center",
  "slug": "community-center",
  "contact_email": "[email protected]",
  "created_at": "2026-01-15T12:00:00.000Z"
}

List donations

curl "https://givebear.io/api/v1/donations?limit=10" \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN"
const { data, has_more, next_cursor } = await gb.donations.list({ limit: 10 });
console.log(data.length, has_more, next_cursor);
page = requests.get(
    "https://givebear.io/api/v1/donations",
    headers=headers,
    params={"limit": 10},
).json()
print(len(page["data"]), page["has_more"], page["next_cursor"])
$ch = curl_init("https://givebear.io/api/v1/donations?limit=10");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
$page = json_decode(curl_exec($ch), true);
curl_close($ch);

echo count($page["data"]), " ", $page["has_more"] ? "true" : "false";
uri = URI("https://givebear.io/api/v1/donations?limit=10")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("GIVEBEAR_TOKEN")}"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
page = JSON.parse(response.body)
puts page["data"].length, page["has_more"]
req, _ := http.NewRequest("GET", "https://givebear.io/api/v1/donations?limit=10", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("GIVEBEAR_TOKEN"))

resp, err := http.DefaultClient.Do(req)
if err != nil {
	panic(err)
}
defer resp.Body.Close()

var page struct {
	Data       []map[string]any `json:"data"`
	HasMore    bool             `json:"has_more"`
	NextCursor *string          `json:"next_cursor"`
}
json.NewDecoder(resp.Body).Decode(&page)
fmt.Println(len(page.Data), page.HasMore)

A list endpoint returns a cursor envelope:

{
  "data": [
    {
      "id": "...",
      "object": "donation",
      "amount_cents": 5000,
      "net_amount_cents": 4750,
      "currency": "usd",
      "donor_name": "Jane Doe",
      "donor_email": "[email protected]",
      "fund_id": "fnd_123",
      "created_at": "2026-06-01T18:30:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ..."
}

Page through results

Lists return at most limit rows (1 to 100, default 25). When has_more is true, pass next_cursor back as cursor to fetch the next page. To sync only recent records, pass updated_since as an ISO-8601 timestamp.

curl "https://givebear.io/api/v1/donations?limit=10&cursor=eyJ..." \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN"

curl "https://givebear.io/api/v1/donations?updated_since=2026-01-01T00:00:00Z" \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN"
// listAll walks every page for you with an async iterator.
for await (const donation of gb.donations.listAll({
  updated_since: "2026-01-01T00:00:00Z",
})) {
  console.log(donation.amount_cents, donation.donor_email);
}
params = {"limit": 10, "updated_since": "2026-01-01T00:00:00Z"}
while True:
    page = requests.get(
        "https://givebear.io/api/v1/donations", headers=headers, params=params
    ).json()
    for donation in page["data"]:
        print(donation["amount_cents"], donation["donor_email"])
    if not page["has_more"]:
        break
    params["cursor"] = page["next_cursor"]
$query = ["limit" => 10, "updated_since" => "2026-01-01T00:00:00Z"];
do {
    $url = "https://givebear.io/api/v1/donations?" . http_build_query($query);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
    $page = json_decode(curl_exec($ch), true);
    curl_close($ch);

    foreach ($page["data"] as $donation) {
        echo $donation["amount_cents"], " ", $donation["donor_email"], "\n";
    }
    $query["cursor"] = $page["next_cursor"];
} while ($page["has_more"]);
params = { limit: 10, updated_since: "2026-01-01T00:00:00Z" }
loop do
  uri = URI("https://givebear.io/api/v1/donations")
  uri.query = URI.encode_www_form(params)
  request = Net::HTTP::Get.new(uri)
  request["Authorization"] = "Bearer #{ENV.fetch("GIVEBEAR_TOKEN")}"

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  page = JSON.parse(response.body)

  page["data"].each { |d| puts "#{d["amount_cents"]} #{d["donor_email"]}" }
  break unless page["has_more"]
  params[:cursor] = page["next_cursor"]
end
cursor := ""
for {
	url := "https://givebear.io/api/v1/donations?limit=10&updated_since=2026-01-01T00:00:00Z"
	if cursor != "" {
		url += "&cursor=" + cursor
	}
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("GIVEBEAR_TOKEN"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	var page struct {
		Data []struct {
			AmountCents int    `json:"amount_cents"`
			DonorEmail  string `json:"donor_email"`
		} `json:"data"`
		HasMore    bool   `json:"has_more"`
		NextCursor string `json:"next_cursor"`
	}
	json.NewDecoder(resp.Body).Decode(&page)
	resp.Body.Close()

	for _, d := range page.Data {
		fmt.Println(d.AmountCents, d.DonorEmail)
	}
	if !page.HasMore {
		break
	}
	cursor = page.NextCursor
}

Handle errors

Failed requests return a non-2xx status and an error envelope with a stable type and a human-readable message:

{
  "error": {
    "type": "invalid_request",
    "message": "`cursor` is malformed"
  }
}
# -f makes curl exit non-zero on HTTP errors; drop it to see the body.
curl -f "https://givebear.io/api/v1/donations?cursor=bad" \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN"
import { ConnectApiError } from "@givebear/connect";

try {
  await gb.donations.list();
} catch (err) {
  if (err instanceof ConnectApiError) {
    console.error(err.status, err.type, err.message);
  }
}
response = requests.get(
    "https://givebear.io/api/v1/donations", headers=headers
)
if not response.ok:
    error = response.json()["error"]
    print(response.status_code, error["type"], error["message"])
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
    $error = json_decode($body, true)["error"];
    echo $status, " ", $error["type"], " ", $error["message"];
}
unless response.is_a?(Net::HTTPSuccess)
  error = JSON.parse(response.body)["error"]
  puts "#{response.code} #{error["type"]} #{error["message"]}"
end
if resp.StatusCode >= 400 {
	var envelope struct {
		Error struct {
			Type    string `json:"type"`
			Message string `json:"message"`
		} `json:"error"`
	}
	json.NewDecoder(resp.Body).Decode(&envelope)
	fmt.Println(resp.StatusCode, envelope.Error.Type, envelope.Error.Message)
}

What you just did

You created a scoped gb_live_ API key, read donations and your organization profile over https://givebear.io/api/v1, and paged through results. Every request was scoped to the organization that owns the key.

Next steps

Collect a donation on the organization's behalf. The intent settles into the org's own Stripe account, and card data goes straight to Stripe (you stay SAQ-A). This needs the payments:write scope:

curl https://givebear.io/api/v1/payment-intents \
  -H "Authorization: Bearer $GIVEBEAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 5000,
    "fund_id": "fnd_123",
    "donor_email": "[email protected]",
    "donor_name": "Jane Doe"
  }'
const intent = await gb.paymentIntents.create({
  amount_cents: 5000,
  fund_id: "fnd_123",
  donor_email: "[email protected]",
  donor_name: "Jane Doe",
});
intent = requests.post(
    "https://givebear.io/api/v1/payment-intents",
    headers=headers,
    json={
        "amount_cents": 5000,
        "fund_id": "fnd_123",
        "donor_email": "[email protected]",
        "donor_name": "Jane Doe",
    },
).json()
$ch = curl_init("https://givebear.io/api/v1/payment-intents");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $token",
    "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "amount_cents" => 5000,
    "fund_id" => "fnd_123",
    "donor_email" => "[email protected]",
    "donor_name" => "Jane Doe",
]));
$intent = json_decode(curl_exec($ch), true);
curl_close($ch);
uri = URI("https://givebear.io/api/v1/payment-intents")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("GIVEBEAR_TOKEN")}"
request["Content-Type"] = "application/json"
request.body = {
  amount_cents: 5000,
  fund_id: "fnd_123",
  donor_email: "[email protected]",
  donor_name: "Jane Doe",
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
intent = JSON.parse(response.body)
body := strings.NewReader(`{
  "amount_cents": 5000,
  "fund_id": "fnd_123",
  "donor_email": "[email protected]",
  "donor_name": "Jane Doe"
}`)
req, _ := http.NewRequest("POST", "https://givebear.io/api/v1/payment-intents", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("GIVEBEAR_TOKEN"))
req.Header.Set("Content-Type", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
	panic(err)
}
defer resp.Body.Close()

Render Stripe Elements with the returned client_secret, publishable_key, and stripe_account_id.

Was this page helpful?

On this page