Skip to main content

Show Your Raise on Your Own Site

Put the amount raised, the goal, and the investor count from your Wefunder round on your own landing page. One publishable key, one endpoint, a dozen lines on the page. No server, no token, nothing to rotate.

Everything this reads is already public on your Wefunder page, so the key that reads it is public too.

What you need​

  • Your publishable key: pk_live_…, shown on your app's page in the developer portal. Company editors enter the portal directly; nobody has to approve you. If you have no app yet, create one and pick Something else with nothing ticked: read:public is on every app and is all this needs.
  • Your company id: co_….

If you're signed in to Wefunder, the snippet below already has both filled in: your company's id and your app's publishable key (pick another if you have several).

The publishable key is the client id. It is not a secret and may sit in your page source; the client secret (sk_live_…) is a different string and never goes in a browser.

The snippet​

Paste this where the numbers should appear. Signed out, replace the two placeholders with your key and id.

<p id="wf-raise">Loading…</p>
<script>
const WF_KEY = "pk_live_YOUR_PUBLISHABLE_KEY";
const WF_COMPANY = "co_YOUR_COMPANY_ID";

fetch(`https://api.wefunder.com/companies/${WF_COMPANY}`, {
headers: { Authorization: `Bearer ${WF_KEY}` },
})
.then((r) => r.json())
.then(({ data }) => {
const a = data.attributes;
const r = a.current_raise;
const usd = (cents) => (cents / 100).toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
const el = document.getElementById("wf-raise");
if (!r) {
el.textContent = `${usd(a.totals.raised_on_wefunder_all_time_cents)} raised on Wefunder`;
return;
}
const verb = r.testing_the_waters ? "reserved" : "raised";
const people = r.testing_the_waters ? "reservations" : "investors";
el.textContent = `${usd(r.amount_raised_cents)} ${verb} of ${usd(r.funding_target_cents)} · ${r.investor_count} ${people}`;
})
.catch(() => { document.getElementById("wf-raise").textContent = "See our raise on Wefunder"; });
</script>

That is the whole integration. The browser calls Wefunder directly from whatever domain your site is on: GET requests to the API answer cross-origin for any origin, so there is no domain to register. The key is rate-limited per key and per IP, and a static page at any traffic stays far inside the limits (Rate limits).

What comes back​

Trimmed to the fields the snippet reads:

{
"data": {
"id": "co_8Kd0aB3xQ9k2vF8mNp1zT5wY",
"type": "company",
"attributes": {
"name": "Acme Robotics",
"url": "https://wefunder.com/acme",
"raising": true,
"current_raise": {
"amount_raised_cents": 64230000,
"funding_target_cents": 100000000,
"investor_count": 1203,
"oversubscribed": false,
"closes_at": "2026-12-31T23:59:59Z",
"testing_the_waters": false,
"offering_id": "ofr_yw3OhusvpfJP4Pk3wRYV1F2R"
},
"totals": {
"profile_total_raised_cents": 64230000,
"raised_on_wefunder_all_time_cents": 64230000
}
}
}
}
  • current_raise describes your current Wefunder round, computed by the same policy your Wefunder page uses, and is null when no round is live (raising: false). The snippet falls back to the lifetime total in totals then.
  • Money is integer cents in every *_cents field, the same convention as GET /investments. The older dollar-string twins (amount_raised, funding_target, …) are still present on this API version and go away in the next dated one; don't build on them.
  • totals.profile_total_raised_cents is the number on your page's ticker bar; it can be larger than the current round when the page folds in past rounds. Pick the figure you mean.

Wording​

If your round is a Testing the Waters round, people on Wefunder reserve; they do not invest yet. Your copy must say "reserved" and "reservations", never "invested" or "investors", while current_raise.testing_the_waters is true. The snippet switches on that flag; keep the switch if you restyle it. When the round converts to a live Reg CF offering the flag flips to false on its own and the copy follows.

From a server instead​

If you already have a backend and would rather keep Wefunder calls off the page (to cache, to combine with other data, or because you also read investor data), the same key works from a server, and the SDKs take it as the bearer. No token exchange is involved for public reads.

import { Wefunder } from "@wefunder/sdk";

const wf = new Wefunder({ accessToken: process.env.WF_PUBLISHABLE_KEY });
// raw.* returns the transport result; unwrap gives you the response body ({ data: company }).
const { data } = await wf.unwrap(wf.raw.getCompany({ path: { id: process.env.WF_COMPANY_ID } }));
const raise = data.attributes.current_raise;
console.log(raise ? raise.amount_raised_cents : data.attributes.totals.raised_on_wefunder_all_time_cents);

Cache the answer for a few minutes if your page is busy; the numbers change at most every few minutes on Wefunder's side too. If you later want investor rows rather than totals, that is where a server key and the investor sync guide come in.

Rate limits that apply​

LimitValueDoes a widget get near it?
Per publishable key300 requests / 5 minutesOnly on a very busy page. Cache in sessionStorage or on your server if you do.
Per credential1,000 requests / hourSame.
Per IP5,000 requests / hourNo: each visitor is their own IP.

GET /companies/{id} is not in the 60-per-minute Explore bucket, which covers /explore, /offerings/{id} and /companies/search only.

Troubleshooting​

SymptomCause
401The key is not a pk_ publishable key, belongs to a deleted app, or is a pk_test_ key used against live. Copy the live key from the app page.
403 insufficient_scopeThe key reached an endpoint that needs a user or an installation. A publishable key reads public data only.
404Not a co_ id, or the company's profile is not publicly listed.
A CORS error in the browser consoleThe request was not a plain GET, or it sent cookies (credentials: "include"). The snippet above sends neither.
current_raise is nullYour round is not live right now. The snippet shows the lifetime total; hide the element if you prefer.
Numbers are 100× too big or too smallA dollar formatter applied to cents, or the reverse. *_cents fields are integers; divide by 100 to display.

Next​