Your First API Request
Two requests: one against the public deal catalog with a server key, one on behalf of a Wefunder user. Together they exercise both ways the API is authorized.
Prerequisites
- An application in the developer portal, with its
client ID (
pk_…) and client secret (sk_…). See Authentication. curlandjq, or any HTTP client.
Use the _test_ client to work against your sandbox realm,
or the _live_ client to hit real data. The requests below are identical either way.
Step 1: Read the public catalog
A client-credentials token carries only read:public, and that is all the catalog needs.
TOKEN=$(curl -s -X POST "https://api.wefunder.com/oauth/token" \
-d "grant_type=client_credentials" \
-d "client_id=pk_live_YOUR_CLIENT_ID" \
-d "client_secret=sk_live_YOUR_CLIENT_SECRET" \
-d "scope=read:public" | jq -r .access_token)
curl -s "https://api.wefunder.com/explore?sort=most_raised" \
-H "Authorization: Bearer $TOKEN"
Response (trimmed):
{
"data": [
{
"id": "ofr_yw3OhusvpfJP4Pk3wRYV1F2R",
"type": "offering",
"company": "co_8Kd0aB3xQ9k2vF8mNp1zT5wY",
"attributes": {
"company_name": "ZeroBorder Self-Banking Centers",
"url": "https://wefunder.com/zeroborder",
"status": "open",
"testing_the_waters": false,
"intended_security": { "type": "safe", "label": "SAFE" },
"funding_target": "1000000",
"amount_raised": "642300",
"investor_count": 1203,
"closes_at": "2025-04-30T03:59:59Z"
}
}
],
"meta": {
"total_count": 119,
"page_count": 25,
"has_more": true,
"next_cursor": 25
}
}
Money fields are decimal strings, never floats. testing_the_waters: true marks a
round that is taking reservations, not investments.
Step 2: Act on behalf of a user
Send a user through the Authorization Code flow
requesting read:profile read:investments, then call the API with the token they granted.
curl -s "https://api.wefunder.com/users/me" \
-H "Authorization: Bearer at_live_USER_TOKEN"
Response (trimmed; the full field list is on GET /users/me):
{
"data": {
"id": "usr_8Kd0aB3xQ9k2vF8mNp1zT5wY",
"type": "user",
"attributes": {
"username": "john_doe",
"full_name": "John Doe",
"city": "San Francisco",
"country": "USA",
"created_at": "2023-01-15T10:30:00Z"
}
}
}
Users, companies, offerings and investments are identified by prefixed strings (usr_,
co_, ofr_, inv_), not numbers. Store them as opaque strings.
Now list the investments that user can see, which covers their own investments plus the investments into any company they edit:
curl -s "https://api.wefunder.com/investments?per_page=2" \
-H "Authorization: Bearer at_live_USER_TOKEN"
{
"data": [
{
"id": "inv_…",
"status": "executed",
"offering": "ofr_…",
"company": "co_…",
"applied_at": "2026-03-02T18:04:11Z",
"amounts": { "committed_cents": 50000, "in_escrow_cents": 0, "currency": "usd" },
"shares": "295",
"average_share_price": "3.38"
}
],
"meta": {
"mode": "bootstrap",
"has_more": true,
"next_cursor": "…",
"published_through": "2026-09-28T23:59:59Z"
}
}
Save next_cursor and pass it back as ?cursor= to page, or later as a sync point.
The guide Investments: list, sync, retrieve covers
the record shape and syncing.
When something goes wrong
Every error has the same envelope. request_id is in the body; quote it when you
write to support.
{
"error": {
"type": "insufficient_scope",
"message": "This endpoint requires the read:investments scope",
"request_id": "req_err789"
}
}
| Status | error.type | Meaning |
|---|---|---|
| 400 | bad_request | Malformed parameter or cursor |
| 401 | unauthorized | Token missing, invalid, or expired |
| 403 | insufficient_scope / forbidden | Token lacks the scope, or the user cannot see this resource |
| 404 | not_found | No such resource, or not visible to this token |
| 422 | validation_error | A field failed validation; see details.errors |
| 429 | rate_limit_exceeded | Back off; see Rate limiting |
More in Error handling.
Next steps
- Act for a company or syndicate: install your app on it, from the portal or from the API, and call with the installation token.
- Investors and portfolio tools:
GET /portfolioandGET /portfolio/positions, in the Investor Portfolio guide. - Syndicate leads: the Syndicate API and the agent quickstart.
- Founders syncing your own company: Sync investments to your CRM. Install on your company from the portal and mint its token there (no OAuth), then
GET /investmentswith that token returns your company's records and nothing else, and webhooks deliverinvestment.changedwhen there is something new to pull. - Deal aggregators: Build a deal aggregator with the Explore API.