# Custom & in-house clients: protect() (/docs/sdk/protect)



If you call a provider or an in-house wrapper that the auto-instrumentation doesn't already cover,
`protect()` wraps one method so it gets the same pre-flight check and telemetry as a built-in
client. Like everything else, it's **fail-open**: if the wrap fails, your call runs unchanged.

## Python [#python]

```python
import token_police as tp

tp.protect(
    "my_company.llm",     # module path
    "InHouseClient",      # class name
    "complete",           # method name
    True,                 # is it async?
    manual=True,          # required for the provider hint to take effect
    provider="openai",    # hint how to parse usage
)
```

Signature:

```python
protect(module_path, class_name, method_name, is_async, *,
        manual=False, provider=None, target_object=None)
```

* `manual=True` — for clients that don't emit OpenTelemetry spans; TokenPolice records usage itself.
* `provider=...` — a hint (e.g. `"openai"`, `"anthropic"`) that drives usage and cost parsing.
* `target_object=...` — pass a live object to wrap instead of resolving it by import.

<Callout type="warn">
  **`provider=` only takes effect with `manual=True`.** The two are gated together, so
  `provider="openai"` on its own is silently ignored — no error, no warning — and the wrapper
  falls back to guessing the provider from the module path, which for an in-house module usually
  guesses nothing. The result is a call that is metered with no usable usage parsing. If you are
  passing `provider=`, pass `manual=True` with it.
</Callout>

## Node [#node]

The shape matches, with **one difference to watch**: the second argument, `objectPath&#x60;, is a
&#x2A;*`string[]`** — the path of property names walked from the module root, not a single string.

```typescript
import * as tp from "token-police";

tp.protect(
  "my-company-llm",                    // module name
  ["InHouseClient", "prototype"],      // objectPath — an ARRAY, walked from the module root
  "complete",                          // method name
  true,                                // is it async?
  { manual: true, provider: "openai" },// options
);
```

Signature:

```typescript
protect(moduleName, objectPath: string[], methodName, isAsync, options?: {
  manual?: boolean;
  provider?: string;
  streaming?: boolean;
  module?: unknown;   // pass a live module/object instead of resolving by name
})
```

<Callout type="warn">
  **`objectPath` is an array, and it must end on the object that actually owns the method.**
  TokenPolice walks the path property by property from the module root and then reads
  `obj[methodName]` — there is no class-vs-instance magic.

  So for `class InHouseClient { async complete() {…} }`, the method lives on
  `InHouseClient.prototype`, and the path is `["InHouseClient", "prototype"]`. Passing
  `["InHouseClient"]` looks for a **static** `InHouseClient.complete`, doesn't find one, and the
  wrap is skipped with a startup warning — your calls run, unmetered and unchecked. Use
  `["InHouseClient"]` only when the method really is static, and a bare `"InHouseClient"` string
  is never right. A nested target is just a longer walk: `["outer", "Inner", "prototype"]`.
</Callout>

## Next [#next]

<Cards>
  <Card title="Support matrix" href="/docs/integrations/matrix" description="Check whether your client is already covered before reaching for protect()." />
</Cards>
