Give your AI app a memory it can search

Sounder stores your documents and chats as vectors and finds the right passages in milliseconds. Hybrid search, filters and cited answers, all through one API.

npm install sounder

Free for your first million vectors. No card needed.

Vector search float field(vec2 p, float t) { float v = 0.0; vec4 C[4]; C[0] = vec4(540.0, 118.0, 52.0, 0.62); C[1] = vec4(585.0, 330.0, 64.0, 0.85); C[2] = vec4(350.0, 398.0, 40.0, 0.5); C[3] = vec4(664.0, 214.0, 32.0, 0.42); for (int i = 0; i < 4; i++) { float ph = TAU * t / T + float(i) * 1.7; v += blob(p, C[i].xy + vec2(7.0 * cos(ph), 6.0 * sin(ph)), C[i].z * (1.0 + 0.08 * sin(2.0 * ph)), C[i].w); } float heat = max(1.0 - sm(0.0, 0.6, t), sm(2.8, 3.3, t)); v += blob(p, vec2(318.0, 222.0), 60.0 * (1.0 + 0.06 * heat), 0.8 + 0.45 * heat); vec2 S = vec2(62.0, 318.0); vec2 D = vec2(212.0, 222.0); float u = clamp((t - 0.8) / 1.8, 0.0, 1.0); vec2 q = t < 0.6 ? D : mix(S, D, ease(u)); float k = t < 0.6 ? 1.05 * (1.0 - sm(0.0, 0.6, t)) : 1.05 * sm(0.6, 0.9, t); float speed = (u > 0.0 && u < 1.0) ? sin(3.14159 * u) : 0.0; vec2 dir = normalize(D - S); v += blob(p, q, 24.0, k); for (int s = 1; s <= 4; s++) v += blob(p, q - dir * 16.0 * float(s), 24.0 - 4.0 * float(s), k * speed * (0.55 - 0.12 * float(s))); if (t > 2.6 && t < 3.95) { float R = 30.0 + 320.0 * sm(2.6, 3.9, t); v += 0.42 * (1.0 - sm(2.6, 3.95, t)) * exp(-pow(length(p - D) - R, 2.0) / 260.0) * (v > 0.05 ? 1.6 : 1.0); } if (hash(floor(p / vec2(7.0, 9.4)) * vec2(7.0, 3.0)) > 0.992) v = max(v, 0.3); return v; }
search.ts38 ms
const { hits } = await sounder.search({
  query: "refund after 30 days",
  topK: 3,
});
  1. 0.92refunds.mdLate returns
  2. 0.87billing.mdStore credit
  3. 0.74faq.mdRefund window

From your data to a cited answer

Point Sounder at your sources once. It keeps the index fresh as they change, so answers never quote last month.

  1. Data pipeline float field(vec2 p, float t) { if (p.x < 30.0 || p.x > 690.0) return 0.0; float ph = TAU * t / T; // how raw the stream still is: 1 before the first sieve, less after each float raw = 1.0 - 0.34 * step(240.0, p.x) - 0.33 * step(380.0, p.x) - 0.3 * step(520.0, p.x); float soft = 1.0 - 0.34 * sm(220.0, 260.0, p.x) - 0.33 * sm(360.0, 400.0, p.x) - 0.3 * sm(500.0, 540.0, p.x); // turbulence that flows right, periodic in the cycle so the loop closes float wob = sin(p.x * 0.045 - ph * 3.0 + sin(p.x * 0.013 + ph)) * 52.0 + sin(p.x * 0.11 - ph * 5.0) * 18.0; float y = 232.0 + wob * soft; float spread = 14.0 + 40.0 * soft; // grit flows with the stream, 72 columns a cycle, and its pattern repeats over those, so the loop closes float grit = soft > 0.2 ? step(0.55 - 0.3 * (1.0 - soft), hash(vec2(mod(floor((p.x - 504.0 * t / T) / 7.0), 72.0), floor(p.y / 9.4)))) : 1.0; float pulse = 0.55 + 0.45 * pow(0.5 + 0.5 * sin(p.x * 0.06 - ph * 6.0), 3.0); float v = exp(-pow(p.y - y, 2.0) / (spread * spread)) * pulse * (0.55 + 0.45 * grit) * (0.7 + 0.3 * (1.0 - soft)); return v; }
    01

    Connect your sources

    Sync docs, tickets and chat history from a bucket, a database or the API. Changes arrive in seconds.

  2. 02

    Embed and index

    Sounder splits each document into passages, embeds them with the model you pick and stores them with their metadata.

  3. 03

    Search by meaning

    Each query blends vector and keyword ranking, then filters by customer, date or any field you stored.

  4. Streaming tokens float field(vec2 p, float t) { float ph = TAU * t / T; float v = blob(p, vec2(88.0, 230.0), 30.0, 1.05); // the ribbon: a travelling wave from the model to the response, pulses running along it (six a cycle) if (p.x > 116.0 && p.x < 356.0) { float env = sin(3.14159 * (p.x - 116.0) / 240.0); float y = 230.0 + 30.0 * env * sin(p.x * 0.035 - ph * 2.0); float pulse = pow(0.5 + 0.5 * sin(p.x * 0.08 - ph * 6.0), 4.0); v += exp(-pow(p.y - y, 2.0) / 64.0) * (0.3 + 0.75 * pulse); } // the response: four lines; the last is written by the cursor, then it all scrolls up a line if (p.x > 376.0 && p.x < 654.0 && p.y > 130.0 && p.y < 322.0) { float yy = p.y + 46.0 * sm(3.2, 3.8, t); float idx = floor((yy - 160.0 + 23.0) / 46.0); float ly = 160.0 + 46.0 * idx; float on = step(0.32, hash(vec2(floor(p.x / 7.0), 1.0))); float front = t < 0.3 ? 380.0 : t < 3.0 ? mix(380.0, 650.0, (t - 0.3) / 2.7) : 650.0; float filled = idx < 3.0 ? 1.0 : idx < 4.0 ? (t > 3.0 ? 1.0 : 1.0 - sm(front - 6.0, front + 4.0, p.x)) : 0.0; float band = exp(-pow(yy - ly, 2.0) / 49.0); v += band * (on * (0.55 + 0.35 * hash(vec2(floor(p.x / 7.0), 2.0))) * filled + 0.13 * (1.0 - filled) * on); float cx = t < 3.0 ? front : 380.0; float cyy = t < 3.0 ? 298.0 : 298.0 - 46.0 * sm(3.2, 3.8, t) + 46.0 * step(3.8, t); v += blob(p, vec2(cx + 3.0, cyy), 8.0, 1.2 * (t < 3.0 || t > 3.8 ? 1.0 : 0.0)); } return v; }
    04

    Answer with sources

    Stream an answer written from the best passages, with a citation on every claim your users can check.

Your first search in a few lines

Typed clients for TypeScript and Python, and a plain HTTP API for everything else. The same query works in all three.

  • Hybrid ranking is on by default
  • Filter on any metadata field
  • One namespace per customer
  • Answers stream as they are written
See what the API covers
import { Sounder } from "sounder";

const sounder = new Sounder({
  apiKey: process.env.SOUNDER_KEY,
});

const { hits } = await sounder.search({
  index: "help-center",
  query: "refund after 30 days",
  filter: { lang: "en" },
  topK: 3,
});

console.log(hits);
Output3 hits38 ms
[
  { id: "refunds#late", score: 0.92 },
  { id: "billing#credit", score: 0.87 },
  { id: "faq#window", score: 0.74 },
]

The parts you would otherwise build yourself

Retrieval is more than a nearest neighbour lookup. Sounder handles the caching, limits and failover that real traffic needs.

Answers that cite their sources

Sounder pulls the closest passages, writes the answer and marks where every line came from.

RAG answer A question lands among a field of document pages; the three closest pages light up and are pulled in around it, and an answer page beside them writes itself line by line from those three, each part ending in a citation mark. // A page centred at c: 5 columns by 6 cells. Faint paper that flickers, a title, ragged lines of text; lit brightens it // and hot (a read) flashes its text. float page(vec2 p, vec2 c, float s, float lit, float hot, float ph) { vec2 q = p - c + vec2(17.5, 28.2); if (q.x < 0.0 || q.x >= 35.0 || q.y < 0.0 || q.y >= 56.4) return 0.0; float lc = floor(q.x / 7.0); float lk = floor(q.y / 9.4); float hs = hash(vec2(lc + s * 5.0, lk + s)); float v = mix(0.13, 0.28, lit) + 0.05 * sin(ph + hs * 40.0); if (lk < 0.5) { if (lc < 2.0 + floor(2.0 * hash(vec2(s, 1.0)))) v = mix(0.45, 1.1, lit); } else if (lk > 1.5) { if (lc < 2.0 + floor(3.6 * hash(vec2(s, lk)))) v = mix(0.3, 0.72, lit) + 0.4 * hot; } return v; } // The question: a speech bubble 9 columns by 4 cells, two lines of words inside its rim, a tail under its left end. float bubble(vec2 p, vec2 c, float k) { vec2 q = p - c + vec2(31.5, 18.8); if (q.x < 0.0 || q.x >= 63.0 || q.y < 0.0 || q.y >= 47.0) return 0.0; float lc = floor(q.x / 7.0); float lk = floor(q.y / 9.4); float side = (lc < 0.5 || lc > 7.5) ? 1.0 : 0.0; float v = 0.0; if (lk < 3.5) { if (lk < 0.5 || lk > 2.5) v = side > 0.5 ? 0.0 : 0.45; else if (side > 0.5) v = 0.45; else if (lk < 1.5 || lc < 5.5) v = 1.15; } else if (lc > 0.5 && lc < 2.5) v = 0.45; return v * k; } float field(vec2 p, float t) { if (p.x < 24.0 || p.x > 696.0 || p.y < 24.0 || p.y > 436.0) return 0.0; float ph = TAU * t / T; float v = 0.0; // the rest of the field, idle v = max(v, page(p, vec2(70.0, 78.0), 4.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(150.0, 58.0), 5.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(250.0, 60.0), 6.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(390.0, 64.0), 7.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(452.0, 108.0), 8.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(90.0, 162.0), 9.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(404.0, 182.0), 10.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(62.0, 310.0), 11.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(134.0, 360.0), 12.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(204.0, 404.0), 13.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(392.0, 330.0), 14.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(352.0, 404.0), 15.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(450.0, 380.0), 16.0, 0.0, 0.0, ph)); v = max(v, page(p, vec2(66.0, 400.0), 17.0, 0.0, 0.0, ph)); // the three nearest: let go as the next question comes in, lit as the ring reaches them, pulled in, read and sent { float g = t < 1.5 ? 1.0 - ease((t - 0.3) / 0.6) : ease((t - 1.850) / 0.65); float lit = t < 1.0 ? 1.0 - sm(0.3, 0.75, t) : sm(1.379, 1.499, t); float hot = exp(-pow(t - 2.590, 2.0) / 0.006); v = max(v, page(p, mix(vec2(165.0, 175.0), vec2(220.0, 182.0), g), 1.0, lit, hot, ph)); } { float g = t < 1.5 ? 1.0 - ease((t - 0.3) / 0.6) : ease((t - 1.910) / 0.65); float lit = t < 1.0 ? 1.0 - sm(0.3, 0.75, t) : sm(1.459, 1.579, t); float hot = exp(-pow(t - 2.840, 2.0) / 0.006); v = max(v, page(p, mix(vec2(300.0, 350.0), vec2(262.0, 302.0), g), 2.0, lit, hot, ph)); } { float g = t < 1.5 ? 1.0 - ease((t - 0.3) / 0.6) : ease((t - 1.970) / 0.65); float lit = t < 1.0 ? 1.0 - sm(0.3, 0.75, t) : sm(1.587, 1.707, t); float hot = exp(-pow(t - 3.090, 2.0) / 0.006); v = max(v, page(p, mix(vec2(335.0, 125.0), vec2(282.0, 180.0), g), 3.0, lit, hot, ph)); } // the question: the last one fades as the next comes in along the lane and lands v = max(v, bubble(p, vec2(250.0, 238.0), 1.0 - sm(0.3, 0.5, t))); v = max(v, bubble(p, mix(vec2(40.0, 238.0), vec2(250.0, 238.0), ease((t - 0.3) / 0.8)), sm(0.3, 0.45, t))); { float u = clamp((t - 0.3) / 0.8, 0.0, 1.0); float sp = (u > 0.0 && u < 1.0) ? sin(3.14159 * u) : 0.0; vec2 b = mix(vec2(40.0, 238.0), vec2(250.0, 238.0), ease(u)); for (int s = 1; s <= 3; s++) v += blob(p, b - vec2(32.0 + 13.0 * float(s), 0.0), 9.0 - 1.5 * float(s), sp * (0.5 - 0.12 * float(s))); } // the search ring if (t > 1.100 && t < 1.750) { float u = (t - 1.100) / 0.650; float R = 14.0 + 136.0 * (1.0 - (1.0 - u) * (1.0 - u)); v += 0.5 * (1.0 - u) * exp(-pow(length((p - vec2(250.0, 238.0)) * vec2(1.0, 1.0)) - R, 2.0) / 90.0); } // the line to the answer: dotted, on one row of cells; each part runs down it with a short tail if (abs(p.y - 238.500) < 1.0 && p.x > 290.0 && p.x < 535.0 && mod(floor((p.x - 4.0) / 7.0 + 0.5), 2.0) < 0.5) v = max(v, 0.3); { float u = clamp((t - 2.650) / 0.4, 0.0, 1.0); if (u > 0.0 && u < 1.0) { float x = mix(304.0, 520.0, ease(u)); float k = sm(0.0, 0.1, u) * (1.0 - sm(0.85, 1.0, u)); v += blob(p, vec2(x, 238.500), 9.0, 1.2 * k) + blob(p, vec2(x - 14.0, 238.500), 6.0, 0.45 * k); } } { float u = clamp((t - 2.900) / 0.4, 0.0, 1.0); if (u > 0.0 && u < 1.0) { float x = mix(304.0, 520.0, ease(u)); float k = sm(0.0, 0.1, u) * (1.0 - sm(0.85, 1.0, u)); v += blob(p, vec2(x, 238.500), 9.0, 1.2 * k) + blob(p, vec2(x - 14.0, 238.500), 6.0, 0.45 * k); } } { float u = clamp((t - 3.150) / 0.4, 0.0, 1.0); if (u > 0.0 && u < 1.0) { float x = mix(304.0, 520.0, ease(u)); float k = sm(0.0, 0.1, u) * (1.0 - sm(0.85, 1.0, u)); v += blob(p, vec2(x, 238.500), 9.0, 1.2 * k) + blob(p, vec2(x - 14.0, 238.500), 6.0, 0.45 * k); } } // the answer page: faint paper; six lines, each written left to right by a hot head; a citation mark after each part { vec2 q = p - vec2(556.0, 177.0); if (q.x >= 0.0 && q.x < 105.0 && q.y >= 0.0 && q.y < 122.2) { float lc = floor(q.x / 7.0); float lk = floor(q.y / 9.4); float a = 0.15 + 0.04 * sin(ph + hash(vec2(lc, lk)) * 40.0); float clear = 1.0 - sm(0.3, 0.6, t); for (int j = 0; j < 6; j++) { float fj = float(j); float w = fj < 0.5 ? 3.050 : fj < 1.5 ? 3.210 : fj < 2.5 ? 3.370 : fj < 3.5 ? 3.530 : fj < 4.5 ? 3.690 : 3.850; float len = fj < 0.5 ? 13.0 : fj < 1.5 ? 11.0 : fj < 2.5 ? 12.0 : fj < 3.5 ? 9.0 : fj < 4.5 ? 12.0 : 7.0; if (abs(lk - 1.0 - 2.0 * fj) < 0.5) { float hc = min(len, 1.0 + floor(len * clamp((t - w) / 0.160, 0.0, 1.0))); float on = t < 1.0 ? clear : step(w, t) * step(lc, hc); if (lc >= 1.0 && lc < 1.0 + len) a = max(a, 0.72 * on); if (t > w && t < w + 0.160 && abs(lc - hc) < 0.5) a = max(a, 1.2); // the citation mark, two cells after a part's last line if (mod(fj, 2.0) > 0.5 && abs(lc - len - 2.0) < 0.5) a = max(a, 1.15 * (t < 1.0 ? clear : sm(w + 0.160, w + 0.220, t))); } } v = max(v, a); } } return v; }

Repeat questions come back from memory

Popular questions are answered from cache. A write clears only the results it changes.

Cache layer vec3 drop(float t) { vec2 A = vec2(108.0, 250.0), P = vec2(262.0, 250.0), C = vec2(300.0, 250.0), O = vec2(344.0, 252.0), R = vec2(470.0, 262.0), B = vec2(560.0, 316.0); if (t < 0.15) return vec3(A, 0.0); if (t < 0.5) return vec3(mix(A, P, ease((t - 0.15) / 0.35)), 1.2 * sm(0.15, 0.22, t)); if (t < 0.95) return vec3(mix(P, A, ease((t - 0.55) / 0.4)), 1.2 * (1.0 - sm(0.85, 0.95, t)) * step(0.55, t) + 1.2 * step(t, 0.55)); if (t < 1.3) return vec3(A, 0.0); if (t < 1.65) return vec3(mix(A, P, ease((t - 1.3) / 0.35)), 1.2 * sm(1.3, 1.37, t)); if (t < 1.8) return vec3(mix(P, O, (t - 1.65) / 0.15), 1.2); if (t < 2.4) return vec3(mix(O, R, ease((t - 1.8) / 0.6)), 1.2); if (t < 2.9) return vec3(mix(R, B, 1.0 - pow(1.0 - (t - 2.4) / 0.5, 2.0)), 1.2); if (t < 3.35) return vec3(mix(B, C, ease((t - 2.95) / 0.4)), 1.2 * step(2.95, t)); if (t < 3.85) return vec3(mix(C, A, ease((t - 3.4) / 0.45)), 1.2 * (1.0 - sm(3.75, 3.85, t))); return vec3(A, 0.0); } float ring(vec2 p, vec2 c, float t, float t0, float dur, float rmax, float k) { if (t < t0 || t > t0 + dur) return 0.0; float u = (t - t0) / dur; return k * (1.0 - u) * exp(-pow(length((p - c) * vec2(1.0, 1.6)) - 8.0 - rmax * u, 2.0) / 30.0); } float field(vec2 p, float t) { vec2 C = vec2(300.0, 250.0); float v = blob(p, vec2(86.0, 250.0), 22.0, 0.95); // the pool: small and hot, its surface rippling where things land float kept = sm(3.3, 3.45, t) * (1.0 - sm(3.6, 4.1, t)); v += blob(p, C, 44.0, 0.78 + 0.45 * kept); v += ring(p, C, t, 0.5, 0.6, 52.0, 0.55) + ring(p, C, t, 1.65, 0.45, 40.0, 0.35) + ring(p, C, t, 3.35, 0.55, 52.0, 0.5); // the reservoir: wide and deep, denser the deeper it goes, stirring when something sinks in vec2 d = (p - vec2(565.0, 272.0)) / vec2(108.0, 118.0); float body = exp(-pow(max(0.0, length(d) - 0.82), 2.0) / 0.004) * step(length(d), 1.0); float stir = sm(2.4, 2.6, t) * (1.0 - sm(2.9, 3.4, t)); v += body * (0.3 + 0.45 * clamp((p.y - 154.0) / 236.0, 0.0, 1.0) + 0.3 * stir * noise(p * 0.05 + t)); v += ring(p, vec2(470.0, 262.0), t, 2.4, 0.7, 70.0, 0.4); vec3 q = drop(t); return v + blob(p, q.xy, 11.0, q.z); }

2msmedian answer for a cached question

Fair limits for every tenant

Give each key a budget, so one busy customer never slows the rest.

Rate limiting float rate(float x) { float P = 420.0; float b = 0.0; for (int j = -1; j <= 3; j++) b += exp(-pow(x - 420.0 - float(j) * P, 2.0) / 2116.0); return 70.0 + 36.0 * sin(TAU * 3.0 * x / P + 0.3) + 24.0 * sin(TAU * 7.0 * x / P + 1.0) + 190.0 * b; } float field(vec2 p, float t) { float X0 = 60.0, X1 = 655.0, FLOOR = 400.0, LIM = 220.0; if (p.x < X0 || p.x > X1 || p.y > FLOOR) return 0.0; float xx = p.x + 420.0 * t / T; float top = FLOOR - rate(xx); if (p.y < top) return 0.0; if (p.y >= LIM) { float flare = top < LIM ? 0.45 * exp(-pow(p.y - LIM, 2.0) / 50.0) : 0.0; return 0.42 + 0.5 * (p.y - max(top, LIM)) / (FLOOR - max(top, LIM)) + flare; } // above the limit: sheared off, breaking into spray that rises and fades. It moves 60 columns and 30 cells a cycle, // and its pattern repeats over exactly those, so the loop closes. float rise = (LIM - p.y) / (LIM - top + 1.0); float spray = step(0.6, hash(vec2(mod(floor(xx / 7.0), 60.0), mod(floor((p.y + 9.4 * 30.0 * t / T) / 9.4), 30.0)))); return spray * (0.55 - 0.42 * rise); }

Filters run inside the search

Narrow by customer, language or date before ranking, so the top results are always ones you can show.

tenant = "acme"lang = "en"updated < 30d

41,208 passages, 3 returned

Two regions, one endpoint

If a region goes down, queries move to the standby before your users notice.

Failover Requests flow through a router to one of two regions of server racks. The primary region loses power, the next request is turned back at its edge, and the router marks it down and sends that request and every one after it to the standby, which takes over; a strip of served requests shows no empty slot. vec2 atA(float u) { float x = mix(237.0, 420.0, u); return vec2(x, 219.7000 + -106.9000 * smoothstep(251.0, 370.0, x)); } vec2 atB(float u) { float x = mix(237.0, 391.5000, u); return vec2(x, 219.7000 + 109.3000 * smoothstep(251.0, 341.5000, x)); } // a rack of 6 columns by 9 cells: rails, caps, four units with a status light each. act is 0 dark, 0.5 standing by, // 1 serving; hit flashes its lights. float rack(float col, float cell, float c0, float k0, float act, float hit, float ph, float s) { float lc = col - c0; float lr = cell - k0; if (lc < 0.0 || lc > 5.0 || lr < 0.0 || lr > 8.0) return 0.0; float frame = 0.2 + 0.26 * min(act * 2.0, 1.0); if (lc < 0.5 || lc > 4.5 || lr < 0.5 || lr > 7.5) return frame; if (mod(lr, 2.0) < 0.5) return 0.0; if (lc > 3.5) return mix(act, 1.1 + 0.4 * hit, sm(0.6, 0.9, act)); return act * (0.68 + 0.08 * sin(2.0 * ph + hash(vec2(lr + s, lc)) * 30.0)); } // a flash of width w round time s, on the cycle: the nearer of s and its copy a cycle away float at(float t, float s, float w) { float d = t - s; d -= T * floor(d / T + 0.5); return exp(-d * d / w); } // a line's band, at least a column wide and a cell tall float band(float d, float k) { return k * (1.0 - sm(3.6, 4.8, abs(d))); } float field(vec2 p, float t) { if (p.x < 24.0 || p.x > 696.0 || p.y < 24.0 || p.y > 436.0) return 0.0; float ph = TAU * t / T; float col = floor((p.x - 4.0) / 7.0 + 0.5); float cell = floor(p.y / 9.4); float v = 0.0; // the regions' states: the primary boots, stands by, serves, loses power; the standby serves, stands down, takes over float pa = t < 1.0 ? sm(0.3, 0.8, t) : 1.0 - sm(2.8500, 3.1000, t); float actA = t < 1.0 ? 0.5 * sm(0.3, 0.8, t) : (0.5 + 0.5 * sm(1.6, 1.8, t)) * (1.0 - sm(2.8500, 3.1000, t)); float actB = t < 2.6 ? 1.0 - 0.5 * sm(1.95, 2.2, t) : 0.5 + 0.5 * sm(3.3100, 3.5300, t); float flash = exp(-pow(t - 2.8800, 2.0) / 0.0012); float toA = sm(1.6, 1.7, t) * (1.0 - sm(3.3100, 3.4100, t)); float hitA = 0.0; float hitB = 0.0; hitB += at(t, 0.5300, 0.006); hitB += at(t, 1.0925, 0.006); hitB += at(t, 1.6550, 0.006); hitA += at(t, 2.2175, 0.006); hitA += at(t, 2.7800, 0.006); hitB += at(t, 3.7600, 0.006); hitB += at(t, 3.9050, 0.006); hitB += at(t, 4.4675, 0.006); // the floors: faint ground; the primary's goes out with its power float hs = hash(floor(p / vec2(7.0, 9.4)) + 5.0); if (col >= 61.0 && col <= 89.0) { if (cell >= 5.0 && cell <= 18.0) v = max(v, step(0.62, hs) * (0.16 + 0.04 * sin(ph + hs * 40.0)) * pa + 0.6 * flash * step(0.62, hs)); if (cell >= 28.0 && cell <= 41.0) v = max(v, step(0.62, hs) * (0.16 + 0.04 * sin(ph + hs * 40.0))); } v = max(v, rack(col, cell, 64.0, 8.0, actA, hitA, ph, 0.0) + 0.7 * flash * step(0.1, rack(col, cell, 64.0, 8.0, 1.0, 0.0, ph, 0.0))); v = max(v, rack(col, cell, 64.0, 31.0, actB, hitB, ph, 3.0)); v = max(v, rack(col, cell, 73.0, 8.0, actA, 0.0, ph, 1.0) + 0.7 * flash * step(0.1, rack(col, cell, 73.0, 8.0, 1.0, 0.0, ph, 0.0))); v = max(v, rack(col, cell, 73.0, 31.0, actB, 0.0, ph, 4.0)); v = max(v, rack(col, cell, 82.0, 8.0, actA, 0.0, ph, 2.0) + 0.7 * flash * step(0.1, rack(col, cell, 82.0, 8.0, 1.0, 0.0, ph, 0.0))); v = max(v, rack(col, cell, 82.0, 31.0, actB, 0.0, ph, 5.0)); // the replication line: dotted, from the primary's floor to the standby's box; copies run from the serving region if (abs(p.x - 501.0) < 1.0 && p.y > 184.6000 && p.y < 241.2000) v = max(v, mod(cell, 2.0) < 0.5 ? 0.32 : 0.0); { float u = clamp((t - 0.8000) / 0.25, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(501.0, mix(229.2000, 190.6000, ease(u))), 7.0, 1.0 * sm(0.0, 0.15, u) * (1.0 - sm(0.85, 1.0, u))); } { float u = clamp((t - 1.0500) / 0.25, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(501.0, mix(229.2000, 190.6000, ease(u))), 7.0, 1.0 * sm(0.0, 0.15, u) * (1.0 - sm(0.85, 1.0, u))); } { float u = clamp((t - 1.3000) / 0.25, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(501.0, mix(229.2000, 190.6000, ease(u))), 7.0, 1.0 * sm(0.0, 0.15, u) * (1.0 - sm(0.85, 1.0, u))); } { float u = clamp((t - 2.0) / 0.25, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(501.0, mix(190.6000, 229.2000, ease(u))), 7.0, 1.0 * sm(0.0, 0.15, u) * (1.0 - sm(0.85, 1.0, u))); } { float u = clamp((t - 2.4000) / 0.25, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(501.0, mix(190.6000, 229.2000, ease(u))), 7.0, 1.0 * sm(0.0, 0.15, u) * (1.0 - sm(0.85, 1.0, u))); } // the users: a crowd of faint points that flicker, and the dotted way in to the router { vec2 d = (p - vec2(64.0, 219.7000)) / vec2(38.0, 100.0); if (length(d) < 1.0) v = max(v, step(0.6, hs) * (0.46 + 0.12 * sin(ph + hs * 50.0)) * (1.0 - 0.35 * length(d))); } if (abs(p.y - 219.7000) < 1.0 && p.x > 108.0 && p.x < 201.0) v = max(v, mod(col, 2.0) < 0.5 ? 0.3 : 0.0); // the router: a dense core in a ring, and its two branches; the one it sends down is lit v += blob(p, vec2(221.0, 219.7000), 13.0, 1.1 + 0.4 * exp(-pow(t - 3.3100, 2.0) / 0.003)); v += 0.5 * exp(-pow(length(p - vec2(221.0, 219.7000)) - 22.0, 2.0) / 16.0); if (p.x > 237.0 && p.x < 420.0) v = max(v, band(p.y - atA(clamp((p.x - 237.0) / 183.0, 0.0, 1.0)).y, 0.26 + 0.32 * toA)); if (p.x > 237.0 && p.x < 391.5000) v = max(v, band(p.y - atB(clamp((p.x - 237.0) / 154.5000, 0.0, 1.0)).y, 0.26 + 0.32 * (1.0 - toA))); // the requests: in from the crowd (one crosses the seam), then down a branch; the one sent to the dead primary is // turned back at its edge, and resent to the standby for (int j = 0; j < 8; j++) for (int w = 0; w <= 1; w++) { float a = 0.0800 + 0.5625 * float(j) + float(w) * T; float u = clamp((t - a + 0.35) / 0.35, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(mix(108.0, 207.0, u), 219.7000), 8.0, 1.15 * sm(0.0, 0.1, u)); } { float u = clamp((t - 0.0800) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atB(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 0.6425) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atB(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 1.2050) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atB(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 1.7675) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atA(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 2.3300) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atA(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 2.8925) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atA(ease(u)), 8.0, 1.15); float b = clamp((t - 3.2925) / 0.18, 0.0, 1.0); if (b > 0.0 && b < 1.0) v += blob(p, atA(1.0) - vec2(22.0 * (1.0 - pow(1.0 - b, 3.0)), 0.0), 8.0, 1.15 * (1.0 - b)); float r = clamp((t - 3.3100) / 0.4000, 0.0, 1.0); if (r > 0.0 && r < 1.0) v += blob(p, atB(ease(r)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, r))); } { float u = clamp((t - 3.4550) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atB(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } { float u = clamp((t - 4.0175) / 0.4000, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, atB(ease(u)), 8.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); } // the strip: a slot opens at the right as each request reaches the router, and fills, in its region's row, once // that request is served float n = 0.0; n += ease((t - 0.0800) / 0.3); n += ease((t - 0.6425) / 0.3); n += ease((t - 1.2050) / 0.3); n += ease((t - 1.7675) / 0.3); n += ease((t - 2.3300) / 0.3); n += ease((t - 2.8925) / 0.3); n += ease((t - 3.4550) / 0.3); n += ease((t - 4.0175) / 0.3); float k = cell - 5.0; if (p.x > 100.0 && p.x < 350.0 && k > -0.5 && k < 2.5 && mod(k, 2.0) < 0.5) { float q = floor(n - 1.0 - (330.0 - p.x) / 14.0 + 0.5); float cx = 330.0 - 14.0 * (n - 1.0 - q); if (abs(p.x - cx) < 3.5 && cx > 106.0 && cx < 344.0) { float j = mod(q, 8.0); float inA = (abs(j - 3.0) < 0.5 || abs(j - 4.0) < 0.5) ? 1.0 : 0.0; float s = j < 0.5 ? 0.4800 : j < 1.5 ? 1.0425 : j < 2.5 ? 1.6050 : j < 3.5 ? 2.1675 : j < 4.5 ? 2.7300 : j < 5.5 ? 3.7100 : j < 6.5 ? 3.8550 : 4.4175; float done = q < -0.5 ? 1.0 : sm(s, s + 0.06, t); float wt = clamp((cx - 106.0) / 14.0, 0.0, 1.0) * clamp((344.0 - cx) / 14.0, 0.0, 1.0); float mine = k < 0.5 ? inA : 1.0 - inA; v = max(v, wt * (0.18 + (mine * 0.92) * done)); } } return v; }
  • 38ms

    Median search across 100M vectors

  • 99.99%

    Uptime over the last 12 months

  • 2.1B

    Vectors stored by 900 teams

  • 11

    Regions to keep data near users

Teams that stopped tuning search by hand

  • Halyard
    Our support bot stopped making up refund rules the week we moved retrieval to Sounder.

    Mara Lindqvist, Head of Support Engineering

  • Quarrystone
    Forty million contract clauses, and search still comes back before the page finishes painting.

    Dev Raman, Staff Engineer

  • Kitewing
    We deleted two services and a cron job. The filters alone paid for the move.

    Ines Okafor, CTO

  • Bramblebook
    Each customer gets their own namespace, so nobody ever sees another school’s notes.

    Tomas Weller, Platform Lead

  • Saltmarsh Health
    Clinicians get an answer with the guideline it came from. That is the only kind they trust.

    Priya Natarajan, Product Director

  • Ottermail
    Inbox search went from keyword roulette to finding the thread you meant on the first try.

    Sam Achterberg, Engineering Manager

Pay for what you store and search

The first million vectors and 100,000 queries are free every month. After that you pay for usage, and you can see the bill coming.

Estimate your monthly bill

25M
2M
Storage $0.80 per million vectors
$19.20
Queries $4 per million queries
$7.60

Estimated monthly bill

$26.80
Usage billing Usage drops fall into a graduated basin and raise its level a step at a time as the month's days pass; at the month's end the basin drains through a narrow outlet into a meter that prints the invoice slip line by line, and the total shows once it is out. float xl(float y) { return 90.0 + 45.0 * (y - 80.0) / 215.0; } float xr(float y) { return 310.0 - 45.0 * (y - 80.0) / 215.0; } // the slip's print at its own row r (0 at the top) and column j (0 to 17) float slip(float r, float j) { // pale paper, so the print reads: a header, four line items a row apart, a rule, the total, a torn foot float P = 0.12; float v = P; if (j < 0.5 || j > 16.5 || r < 0.5) v = 0.6; // its edges else if (r < 1.5) v = j > 1.5 && j < 9.5 ? 1.15 : (j > 11.5 && j < 15.5 ? 0.6 : P); // header, date else if (abs(r - 4.0) < 0.5 || abs(r - 16.0) < 0.5) v = mod(j, 2.0) < 0.5 ? 0.6 : P; // rules else if (r > 5.5 && r < 14.5 && mod(r - 6.0, 3.0) < 0.5) { // four line items: what was used, and its amount float len = floor(hash(vec2(r, 3.0)) * 4.0); v = j > 1.5 && j < 5.5 + len ? 0.95 : (j > 11.5 && j < 15.5 ? 1.05 : P); } else if (abs(r - 19.0) < 0.5) v = j > 1.5 && j < 6.5 ? 1.15 : (j > 9.5 && j < 15.5 ? 1.2 : P); // the total else if (abs(r - 23.0) < 0.5) v = mod(j, 2.0) < 0.5 ? 0.6 : 0.0; // torn foot return v; } float field(vec2 p, float t) { if (p.x < 18.0 || p.x > 702.0 || p.y < 18.0 || p.y > 442.0) return 0.0; float ph = TAU * t / T; float k = floor(p.y / 9.4); float c = floor((p.x - 4.0) / 7.0 + 0.5); float v = 0.0; // the level, in graduations: up one per landed drop, down as it drains float lvl = ease((t - 0.47) / 0.15) + ease((t - 0.77) / 0.15) + ease((t - 1.07) / 0.15) + ease((t - 1.37) / 0.15) + ease((t - 1.67) / 0.15) + ease((t - 1.97) / 0.15); float dr = ease((t - 2.3) / 1.1); lvl -= 6.0 * dr; float surf = 31.0 - 3.0 * lvl; // the surface row float draining = step(2.3, t) * step(t, 3.4); float open = sm(2.25, 2.32, t) * (1.0 - sm(3.5, 3.6, t)); // the feed rail: a churning manifold over a dense bar, six nozzles under it if (p.x > 126.0 && p.x < 262.0) { if (k == 3.0) v += 0.3 + 0.3 * noise(vec2(p.x * 0.09 + 2.0 * cos(ph), 2.0 * sin(ph))); if (k == 4.0) v += 1.05; if (k == 5.0 && mod(c - 19.0, 3.0) < 0.5) v += 0.6; } // the basin: slanted walls with a lip, a floor with the outlet in it float L = xl(p.y), R = xr(p.y); if (k >= 8.0 && k <= 30.0) { if ((p.x >= L - 8.0 && p.x < L) || (p.x > R && p.x <= R + 8.0)) v += 1.05; else if ((p.x >= L - 16.0 && p.x < L) || (p.x > R && p.x <= R + 16.0)) v += 0.7; if (k == 8.0 && ((p.x >= L - 30.0 && p.x < L - 16.0) || (p.x > R + 16.0 && p.x <= R + 30.0))) v += 1.0; } if ((k == 31.0 || k == 32.0) && p.x >= L - 16.0 && p.x <= R + 16.0) { if (c == 28.0) v += 1.05 * (1.0 - open) + 0.95 * open; else v += k == 31.0 ? 1.0 : 0.7; } // graduations: ticks out from the left wall, long every other one, and a dotted level line inside at each for (int i = 0; i <= 6; i++) { float gk = 31.0 - 3.0 * float(i); if (k == gk && i > 0) { bool major = mod(float(i), 2.0) < 0.5; if (p.x < L - 16.0 && p.x >= L - 16.0 - (major ? 21.0 : 10.0)) v += major ? 0.7 : 0.6; if (p.x > L + 3.0 && p.x < R - 3.0 && mod(c, 2.0) < 0.5) v += 0.18; } if (k == gk && i == 0 && p.x < L - 16.0 && p.x >= L - 37.0) v += 0.7; } // the contents: a dense surface, denser with depth, streaming toward the outlet while it drains if (k < 31.0 && p.x >= L && p.x <= R && k >= surf - 0.5) { float depth = clamp((k - surf) / 18.0, 0.0, 1.0); v += k < surf + 0.5 ? 1.05 : 0.42 + 0.3 * depth; float d = length((p - vec2(200.0, 294.9)) / vec2(1.0, 1.6)); v += draining * 0.35 * step(0.62, fract(d / 26.0 + 5.0 * t)) * sm(2.3, 2.4, t); } { // drop 1: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 0.15) / 0.32; float g = (t - 0.03) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(179.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(179.0, mix(58.0, 285.4, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 0.47) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 27.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 179.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 179.0) - 9.0) < 4.0) v += 0.7; } } { // drop 2: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 0.45) / 0.32; float g = (t - 0.33) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(221.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(221.0, mix(58.0, 257.2, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 0.77) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 24.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 221.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 221.0) - 9.0) < 4.0) v += 0.7; } } { // drop 3: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 0.75) / 0.32; float g = (t - 0.63) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(137.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(137.0, mix(58.0, 229.0, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 1.07) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 21.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 137.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 137.0) - 9.0) < 4.0) v += 0.7; } } { // drop 4: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 1.05) / 0.32; float g = (t - 0.93) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(200.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(200.0, mix(58.0, 200.8, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 1.37) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 18.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 200.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 200.0) - 9.0) < 4.0) v += 0.7; } } { // drop 5: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 1.35) / 0.32; float g = (t - 1.23) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(242.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(242.0, mix(58.0, 172.6, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 1.67) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 15.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 242.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 242.0) - 9.0) < 4.0) v += 0.7; } } { // drop 6: forms at its nozzle, falls gathering speed, lands on the surface, ripples out float u = (t - 1.65) / 0.32; float g = (t - 1.53) / 0.12; if (g > 0.0 && u < 0.0) v += blob(p, vec2(158.0, 58.0), 8.0, 1.1 * g); if (u >= 0.0 && u < 1.0) v += blob(p, vec2(158.0, mix(58.0, 144.4, u * u)), 9.0, 1.15 * (1.0 - sm(0.9, 1.0, u))); float w = (t - 1.97) / 0.4; if (w > 0.0 && w < 1.0) { float sk = 12.0; if (k == sk && p.x > L && p.x < R) v += 0.7 * (1.0 - w) * exp(-pow(abs(p.x - 158.0) - 120.0 * w, 2.0) / 120.0); if (k == sk - 1.0 && w < 0.4 && abs(abs(p.x - 158.0) - 9.0) < 4.0) v += 0.7; } } // the outlet and the pipe: narrow walls, and the flow along them once the outlet is open float wl = 0.42; if (c == 27.0 && k > 32.0 && k <= 41.0) v += wl; if (c == 29.0 && k > 32.0 && k <= 39.0) v += wl; if (k == 39.0 && c > 29.0 && c < 62.0) v += wl; if (k == 41.0 && c >= 27.0 && c < 62.0) v += wl; float sd = -1.0; // how far along the flow's path this cell is if (c == 28.0 && k > 32.0 && k <= 40.0) sd = (k - 32.0) * 9.4; if (k == 40.0 && c > 28.0 && c < 62.0) sd = 75.2 + (c - 28.0) * 7.0; if (sd >= 0.0) { float at = sd / 1566.0; float on = step(2.3 + at, t) * step(t, 3.4 + at); v += on * (fract((sd - 900.0 * t) / 28.0) < 0.55 ? 1.05 : 0.55); } // the meter: a slot on top, a body with vents, its intake and a light that is on while it prints float printing = step(2.5, t) * step(t, 3.6); if (c >= 62.0 && c <= 88.0 && k >= 38.0 && k <= 42.0) { if (k == 38.0) v += c >= 66.0 && c <= 85.0 ? 0.2 : 1.0; else if (k == 39.0 || k == 42.0) v += 0.7; else if (c >= 86.0 && c < 88.0) v += 0.42 + 0.68 * printing; else if (c == 62.0) v += 0.42 + 0.63 * step(2.5, t) * step(t, 3.6); else v += mod(c, 2.0) < 0.5 ? 0.7 : 0.3; } // the slip: last month's lifts off and fades at the month's end; the new one comes out of the slot a row at a time if (c >= 67.0 && c <= 84.0 && k < 38.0) { float top = 13.0, a = 1.0, rows = 25.0; if (t >= 2.12 && t < 2.5) { float u = clamp((t - 2.12) / 0.28, 0.0, 1.0); top -= floor(2.0 * ease(u) + 0.5); a = 1.0 - ease(u); } else if (t >= 2.5) { rows = floor(25.0 * ease((t - 2.5) / 1.1) + 0.001); top = 38.0 - rows; } float r = k - top; if (r >= 0.0 && r < rows) v += a * slip(r, c - 67.0); } // the month: 30 days, each lit as it passes; day 30 the month's end; cleared left to right for the new month if (k == 45.0 && c >= 14.0 && c < 44.0) { float d = c - 13.0; float lit = step(0.15 + 0.065 * d, t) * (1.0 - step(3.62 + 0.006 * d, t)); v += 0.2 + lit * (d > 29.5 ? 0.9 : 0.55); } return v; }
  • Free 1M vectors and 100K queries each month
  • Budgets Email at 80% and 100%, hard caps per key
  • Enterprise Private regions, SSO and a 99.99% SLA

Questions

Not answered here? Write to us and an engineer will reply.

What counts as a vector?

One embedded passage with its text and metadata. A 2,000 word document usually becomes 8 to 12 vectors.

Which embedding models can I use?

Bring your own vectors, or let Sounder embed for you with one of its hosted open models. You can switch models later and re-embed in the background.

Do I still need a keyword search engine?

No. Every query blends vector and keyword ranking, and you can weight one over the other per query.

How fresh is the index?

A write is searchable in about a second. Synced sources check for changes every minute, or right away when they send a webhook.

Where is my data stored?

In the region you pick, encrypted at rest and in transit. Enterprise plans can run in a private region inside your own cloud account.

What happens if I go over my estimate?

Nothing stops. You get an email at 80% and 100% of a budget you set, and you can put a hard cap on any key.