Go from zero to processing audio: create your Sanas Console account, spin up a project, generate an API key, install the SDK, run your first inference, and watch usage and credit draw down.
The Sanas Console is where you manage everything outside your code — your account, projects, API keys, credit balance, and usage. The Sanas SDK is what runs in your code: you feed it interleaved float32 PCM frames and it returns processed frames — noise-cancelled, speech-enhanced, accent-converted, or translated into another language.This guide takes you all the way through: create an account and API key in the Console, install the SDK, process your first audio stream, then see that activity show up as usage and credit in the Console.
Time required: about 10 minutes. You need: a work email address you can receive mail at, and a supported Python interpreter. No credit card — new accounts start with $10.00 in free credit.
Sanas signs you in with a one-time passcode instead of a password. Check your inbox for a 6-digit code and enter it, then select Verify.
The code is time-limited. If it lapses, use Resend Passcode — the link becomes available after the countdown finishes. Change lets you correct the address before requesting a new code.
Once verified, your account is created and you’re taken straight into the Console.
New accounts land on Account & Billing — your prepaid credit wallet. Usage is metered per minute and drawn down from this balance in real time.
Account & Billing — the prepaid credit wallet
What’s on this page:
Section
What it tells you
Current balance
Credit available right now. New accounts start at $10.00.
Free credit notice
Your promotional credit and its expiry date.
Payment method
Add a card (handled by Stripe) to buy more credit and enable auto-reload.
Auto-reload
Off until a card is on file. Tops the wallet back up automatically.
Monthly spend cap
A safety limit for the account, independent of auto-reload — usage and top-ups pause once you reach it in a calendar month. Editable via the pencil icon.
Usage this month
Per-product rates, minutes consumed, and cost so far.
Billing history
Every credit purchase and top-up, with invoices on Stripe.
Free credit is enough to start developing immediately. You only need a card when you want to purchase additional credit.
Creating the project drops you straight into it. The left sidebar switches from account scope to project scope — note the Project ID under the project name, and the new project-only entries such as Sub Projects and API Keys.
Project home — the sidebar is now scoped to the project
Every project has its own Project ID (visible in the sidebar and under the dashboard heading, with a copy button). Keep it handy — it identifies the project in support conversations and reporting.
Step 7 — Copy your key and store it somewhere safe
This is the only time the full key is shown. Copy it now and store it in a secrets manager, your CI/CD secret store, or your team’s password manager before you close this dialog. Sanas cannot show it to you again — if you lose it, your only option is to create a replacement key and update every integration that used the old one.
Use the copy icon to grab the key, paste it into your secret store, then select Done.
Never commit an API key to source control, paste it into a ticket or chat, or ship it in client-side code. Load it from an environment variable or secret manager at runtime.
Your key now appears in the project’s API Keys list — permanently masked, showing only its status, expiry, creation date, and last-used timestamp.
The API Keys list — the key is masked from here on
Use the ⋮ menu on any row to edit or revoke a key. The Last used column is the quickest way to spot keys that are no longer in service and can be retired.
With an API key in hand, switch from the Console to your code. The Python package ships as a self-contained wheel: everything the SDK needs is bundled inside it, so there’s no separate native library to install.
The SDK does not open microphones or speakers for you — you bring your own audio (from a file, socket, or media stream). You push frames in with process_frame and read processed frames back.
Select Get SDK at the bottom of the sidebar. Choose your platform and download the SDK package. The same dialog links to the full SDK usage documentation.
Get SDK — download the package for your platform
Before you begin — pick the right wheel
You only need a supported CPython interpreter and pip — no extra tooling required. The wheel in your archive targets the Python version named in its filename:
Python version
Wheel tag
Notes
3.10
cp310-cp310
version-specific
3.11
cp311-cp311
version-specific
3.12, 3.13, 3.14
cp312-abi3
one Stable-ABI wheel covers 3.12+
Pick the archive whose <pytag> matches your interpreter (e.g. cp310 for Python 3.10; cp312 for Python 3.12 or newer).
The SDK runs in one of two modes. The setup flow is identical — you always create_sdk → activate_api_key → create_audio_processor and wait for PipelineState.RUNNING — but how you select the mode and what you get back differ:
Audio Processing
Language Translation (LT)
Selected by
a model key on AudioAttributes.model_name (e.g. SE2.2, AT5.2)
higher (~seconds) — drain with a longer silence window
For audio processing, set model_name to one of the model keys enabled for your account:
Category
Model keys
Agentic Speech Enhancement
AGENTIC_ST_SE, AGENTIC_VI_GT_SE, AGENTIC_VI_G_SE
Accent Translation (AT)
AT5.2
Speech Enhancement
SE1.2, SE2.1, SE2.2, VI_G_SE
This page covers Language Translation inside the Python SDK (selected via lt_config). Sanas also offers a standalone, browser-friendly Language Translation API (JavaScript client) for real-time speech-to-speech in the browser — use that instead of the SDK if you’re building a web app.
The pipeline initializes asynchronously. After you create a processor, wait for PipelineState.RUNNING (via the audio_pipeline_state_notify callback) before feeding frames.
process_frame is synchronous but expects real-time audio. Feed frames at the rate they would arrive live (one 20 ms frame every 20 ms). The snippets below pace with a monotonic-clock deadline so timing doesn’t drift. The two tabs below show the only difference between the modes: Audio Processing sets a model_name; Language Translation leaves it empty and passes an lt_config instead. Pass the API key you copied in Step 7 as YOUR_API_KEY.
Audio Processing
Language Translation
Create sdk_example.py and set model_name to one of your enabled model keys.
import arrayimport threadingimport timeimport sanasfrom examples.wav_utils import read_wav, save_wav # or copy these helpers# 1. Create + activate (activation is synchronous and returns an SdkResult).sdk = sanas.create_sdk(sanas.InitParams(storage_dir="./storage"))res = sdk.activate_api_key("YOUR_API_KEY")if not res.success: raise RuntimeError(f"activation failed ({res.error_type}): {res.message}")# 2. Read input audio as interleaved float32.float32_bytes, sample_rate, channels = read_wav("input.wav")samples = array.array("f"); samples.frombytes(float32_bytes)# 3. Get notified when the pipeline is ready. The state callback fires on a# background thread, so publish each state under a Condition and let the# main thread wait for a terminal state (RUNNING = go, NOT_RUNNING = failed).cond = threading.Condition()state = {"value": None}def on_state(s): with cond: state["value"] = s cond.notify_all()attrs = sanas.ProcessorAttributes( audio_attributes=sanas.AudioAttributes( sampling_rate=sample_rate, channels=channels, model_name="<your-model-key>", # e.g. "AT5.2", "SE2.2", "VI_G_SE" audio_pipeline_state_notify=on_state, ))# 4. Feed 20 ms frames at real time; process_frame returns the processed frame.frame_len = sample_rate * channels // 50 # samples in 20 msframe_period = 0.020 # seconds per frameout_bytes = []_terminal = (sanas.PipelineState.RUNNING, sanas.PipelineState.NOT_RUNNING)with sdk.create_audio_processor(attrs) as proc: with cond: # wait until the pipeline settles cond.wait_for(lambda: state["value"] in _terminal, timeout=30) if state["value"] != sanas.PipelineState.RUNNING: raise RuntimeError(f"pipeline did not start (state: {state['value']})") # `deadline` is the wall-clock time the NEXT frame should be sent. deadline = time.monotonic() for i in range(0, len(samples) - frame_len + 1, frame_len): frame = proc.process_frame(sanas.AudioFrame(samples=samples[i:i + frame_len])) if frame.frame_count: out_bytes.append(bytes(frame.samples)) # memoryview is transient; copy deadline += frame_period time.sleep(max(0.0, deadline - time.monotonic())) # Drain the buffered tail by pushing silence (still paced at real time). silence = array.array("f", bytes(frame_len * 4)) for _ in range(10): frame = proc.process_frame(sanas.AudioFrame(samples=silence)) if frame.frame_count: out_bytes.append(bytes(frame.samples)) deadline += frame_period time.sleep(max(0.0, deadline - time.monotonic()))save_wav("output.wav", b"".join(out_bytes), sample_rate, channels)
examples/helpers.py provides PipelineWaiter, sleep_until, and feed_and_drain, which wrap steps 3–4 above — prefer them over hand-rolling the loop.
Language Translation is selected by the presence of lt_config (not by a model key), so leave model_name empty. Translated audio comes back from process_frame; transcripts arrive on the lt_config callback.
import arrayimport threadingimport timeimport sanasfrom examples.wav_utils import read_wav, save_wavsdk = sanas.create_sdk(sanas.InitParams(storage_dir="./storage"))res = sdk.activate_api_key("YOUR_API_KEY")if not res.success: raise RuntimeError(f"activation failed ({res.error_type}): {res.message}")float32_bytes, sample_rate, channels = read_wav("input.wav")samples = array.array("f"); samples.frombytes(float32_bytes)def on_transcript(tf): kind = "Translation" if tf.type_ == sanas.TranscriptType.TRANSLATION else "Transcription" for seg in tf.transcript_data_.complete: print(f"[{kind}] {seg.text}")cond = threading.Condition()state = {"value": None}def on_state(s): with cond: state["value"] = s cond.notify_all()attrs = sanas.ProcessorAttributes( audio_attributes=sanas.AudioAttributes( sampling_rate=sample_rate, channels=channels, model_name="", # LT is selected by lt_config, not a key audio_pipeline_state_notify=on_state, ), lt_config=sanas.LanguageTranslationConfig( language_in="en-US", language_out="es-ES", conversation_id="", # optional; links two-party sessions callback=on_transcript, ),)frame_len = sample_rate * channels // 50 # samples in 20 msframe_period = 0.020 # seconds per frameout_bytes = []_terminal = (sanas.PipelineState.RUNNING, sanas.PipelineState.NOT_RUNNING)with sdk.create_audio_processor(attrs) as proc: with cond: cond.wait_for(lambda: state["value"] in _terminal, timeout=30) if state["value"] != sanas.PipelineState.RUNNING: raise RuntimeError(f"pipeline did not start (state: {state['value']})") deadline = time.monotonic() for i in range(0, len(samples) - frame_len + 1, frame_len): frame = proc.process_frame(sanas.AudioFrame(samples=samples[i:i + frame_len])) if frame.frame_count: out_bytes.append(bytes(frame.samples)) deadline += frame_period time.sleep(max(0.0, deadline - time.monotonic())) # Translation adds several seconds of latency, so keep pushing silence for a # few seconds after the input ends to pull back the translated tail. silence = array.array("f", bytes(frame_len * 4)) for _ in range(int(5.0 / frame_period)): # ~5 s of silence frame = proc.process_frame(sanas.AudioFrame(samples=silence)) if frame.frame_count: out_bytes.append(bytes(frame.samples)) deadline += frame_period time.sleep(max(0.0, deadline - time.monotonic()))save_wav("translated.wav", b"".join(out_bytes), sample_rate, channels)
Because translation runs speech-to-text, translation, and text-to-speech end to end, it adds several seconds of latency. Keep the extended silence-drain loop (~5 s) so the translated tail is pulled back after your input ends — this is the main runtime difference from audio processing.
Building for the browser instead of a Python backend? Use the standalone Language Translation API (JavaScript client) rather than the SDK path shown here.
When the SDK runs on cloud / remote inference (the default), you can pass a CloudInferencingParams on AudioAttributes.cloud_inferencing_params to tune the remote path. These parameters are only meaningful for cloud inference and are ignored for local inference. The field is optional — omit it to keep the defaults.
Field
Type
Default
Meaning
use_pcm16
bool
False
Negotiate L16 (raw 16-bit linear PCM) at the session sample rate instead of the rate-default codec. Preserves fidelity and avoids server-side transcoding, at the cost of more bandwidth.
cloud_params = sanas.CloudInferencingParams()cloud_params.use_pcm16 = True # opt into raw 16-bit linear PCM uplinkattrs = sanas.ProcessorAttributes( audio_attributes=sanas.AudioAttributes( sampling_rate=sample_rate, channels=channels, model_name="<your-model-key>", cloud_inferencing_params=cloud_params, # optional; omit for defaults audio_pipeline_state_notify=on_state, ))
Leave cloud_inferencing_params unset (or use_pcm16=False) unless you specifically need raw PCM uplink — the rate-default codec is the recommended default for most sessions.
Once the SDK is initialized with a valid key and a model, you can start running inference. Every stream you process is metered against the project that owns the key.
The examples/ folder is self-contained (standard-library helpers only, no numpy). With your venv active, set the required environment variables and run an example from inside the extracted archive:
export SANAS_API_KEY="your-key"export SANAS_STORAGE_DIR=./storage # SDK data + logs (created if missing)export SANAS_INPUT_WAV=input.wav # 16-bit PCM or 32-bit float WAVpython examples/sdk_example.py # audio processingpython examples/language_translation_example.py # language translationpython examples/multi_stream_example.py --streams 4 # concurrency benchmark
SANAS_STORAGE_DIR is where the SDK keeps its data and writes logs (under storage_dir/logs).
Step 13 — Switch between projects and your account
The Console has two scopes, and knowing how to move between them is the key to reading your data correctly.
Account scope
Everything across all projects: total usage, the credit wallet, billing, team, and account settings.
Project scope
One project only: its API keys, sub projects, connected clients, and its own usage.
To switch, select the name card at the top of the sidebar. The account sits at the top of the dropdown, with its projects nested beneath it. Choose the account to go account-wide, or any project to scope down. The same switcher is available from the breadcrumb in the header.
The switcher — account on top, projects nested beneath
The dropdown also holds Search Projects… and + Create Project, so this is the fastest way to add projects two and beyond — the automatic first-project dialog only appears once.
You can also browse every project from Projects in the account sidebar, which shows each project’s ID, API key count, connected clients, and creation date.
Open Usage inside the project to see only what that project’s keys processed — streams processed, audio minutes, and average stream duration, with charts over time. Filter by product and by date range.
Project-scoped usage — 12 streams, 14 minutes of audio
Now switch to the account using the sidebar switcher and open Usage there. Same metrics, aggregated across every project — this is the number to watch as you add projects.
Account-scoped usage, aggregated across all projects
With a single project the two views match. Once you add projects, the account view is the only place the totals line up — a project view can never show you the whole bill.
Back on Account & Billing, the same activity shows up as money. Compare this with the fresh wallet in Step 3 — the balance has moved from 10.00∗∗to∗∗9.76:
Current balance drops as minutes are processed.
Spent this cycle rises to match.
Usage this month breaks the spend out per product, at each product’s per-minute rate — here 14m 12s of audio costing $0.24 in total.
The wallet after processing — balance down, spend broken out per product
Because rates differ sharply per product, minutes alone don’t tell you the cost — the same two minutes cost $0.18 on Language Translation but under a cent on Cloud Speech Enhancement. Use the Cost column, not the Usage column, to see where your credit actually goes.
Usage is metered per minute and drawn down in real time, so a burst of inference is visible on both the Usage pages and the wallet shortly after it runs. Reporting is aggregated on the timezone you chose when creating the project.
Usage this month is scoped to the current calendar month, so it resets on the 1st while Current balance keeps falling across months. If the balance has dropped but this panel reads $0.00, the processing happened in an earlier period — widen the range on the Usage page to find it.
When credits run out, the wallet status flips to Paused — paid usage stops until you add credits. Add a payment method and turn on auto-reload before you depend on Sanas in production, so a busy day can’t interrupt your service.
Check spam and any corporate mail filtering. Wait for the resend countdown to finish, then select Resend Passcode. Use Change to correct a typo in the address rather than requesting more codes to the wrong inbox.
I closed the dialog before copying my key
The full key is unrecoverable by design. Create a new key, update your integration, and revoke the old one from the ⋮ menu in the API Keys list.
The first-project dialog didn't appear
It only opens automatically for accounts with no projects. Use Projects → Create Project, or + Create Project in the sidebar switcher.
I don't see the API Keys entry
You’re at account scope. API keys belong to a project — select a project in the sidebar switcher, then choose API Keys.
`import sanas` fails or the wheel won't install
Confirm the wheel tag matches your interpreter (cp310 for Python 3.10; cp312-abi3 for 3.12+). Install from inside the extracted archive so pip install sanas-*.whl resolves the bundled wheel, and make sure your venv is activated first.
The pipeline never reaches RUNNING
Activation and model selection are the usual causes. Check that activate_api_key returned success, that model_name is a key enabled for your account (or that lt_config is set for Language Translation), and that you waited on the audio_pipeline_state_notify callback before feeding frames.
My usage still reads zero
Confirm the date-range filter covers when you ran inference, and that you’re looking at the project whose key the SDK is using. Check at account scope to rule out having run against a different project. Usage is aggregated on the project’s configured timezone, so very recent activity may sit in the current day’s bucket.
Processing stopped unexpectedly
Check Account & Billing. If the status pill reads Paused, either credits ran out or the monthly spend cap was reached for the calendar month. Add credits to resume, or raise the cap with the pencil icon next to it.