---
source_url: https://www.pubnub.com/docs/sdks/c/api-reference/misc
title: Utility Methods API for C SDK
updated_at: 2026-09-24T12:25:41.000Z
sdk_name: PubNub C SDK
sdk_version: 1.0.0
---

# Utility Methods API for C SDK

PubNub C SDK, use the latest version: 1.0.0

## Documentation index

To discover more PubNub resources:

1. Fetch [PubNub's llms.txt](https://www.pubnub.com/llms-full.txt) for a list of available pages in Markdown format.
2. Identify relevant URLs from that index.
3. Fetch the target pages.

Do not assume a path exists, always check the index first.

This page covers the small utilities that have no other home in the API reference: fetching PubNub server time, reading the SDK version, checking which features are compiled in, and pointing the client at custom DNS servers. It also notes where proxy and TCP keepalive behavior are documented, and what has no public equivalent in this SDK at all.

## Server time

### Method(s)

```c
#include <pubnub/features/time.h>

pubnub_future_t pubnub_time(pubnub_context_t* ctx);
```

| Parameter | Direction | Description |
| --- | --- | --- |
| `ctx` | in, borrowed | Must be an already-initialized context (`pubnub_create` or `pubnub_init` already succeeded). |

**Header:** `#include <pubnub/features/time.h>` (or the umbrella `<pubnub/pubnub.h>`) **Types:** `pubnub_future_t`, `pubnub_timetoken_t` **Prerequisite:** an initialized context. **Feature flag:** `PUBNUB_ENABLE_TIME`. This flag is ON only in the `full` build profile. It is OFF in `minimal` and `embedded`, so `pubnub_time()` does not exist in the compiled API on those profiles. See [Feature flags](https://www.pubnub.com/docs/sdks/c/environment-setup.md) for the full per-profile table. **Ownership / lifetime:** the timetoken view returned by the result accessor aliases the response buffer and stays valid until `pubnub_future_release` is called on the same future. **Thread / callback context:** the same three consumption styles as every other feature call, with no operation-specific re-entrancy limit. **Blocking:** `pubnub_time()` itself does not block. It submits an asynchronous request and returns a `pubnub_future_t` immediately. The calling thread only blocks if you choose the blocking consumption style (`pubnub_await`).

### Sample code

Cooperative polling, adapted from the SDK's own `examples/time/cooperative.c`:

```c
#include <pubnub/pubnub.h>

#include <stdio.h>

int main(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key   = "demo";
    cfg.user_id         = "example-time-cooperative";

    pubnub_context_t* ctx = pubnub_create(&cfg);
    if (NULL == ctx) {
        printf("pubnub_create failed\n");
        return 1;
    }

    pubnub_future_t fut = pubnub_time(ctx);

    while (!pubnub_future_is_ready(fut)) {
        pubnub_process(ctx);
    }

    const pubnub_res_t status = pubnub_future_status(fut);
    if (PUBNUB_OK == status) {
        const pubnub_timetoken_t tt = pubnub_time_result_timetoken(fut);
        printf("Server time: %.*s\n", (int)tt.len, tt.ptr);
    } else {
        const pubnub_string_view_t err = pubnub_response_error_message(fut);
        printf("Time request failed: %.*s\n", (int)err.len, err.ptr);
    }

    pubnub_future_release(fut);
    pubnub_destroy(ctx);
    return 0;
}
```

#### Other examples

##### Callback style

Adapted from the SDK's `examples/time/async.c`. The loop uses the same `pubnub_process` pattern as the SDK's other callback-style examples in this documentation, without the internal example helper the source file uses for its sleep interval:

```c
#include <pubnub/pubnub.h>

#include <stdio.h>

static volatile int s_done = 0;

static void on_time_complete(pubnub_future_t future, pubnub_res_t status, void* user_data)
{
    (void)user_data;

    if (PUBNUB_OK == status) {
        const pubnub_timetoken_t tt = pubnub_time_result_timetoken(future);
        printf("Server time: %.*s\n", (int)tt.len, tt.ptr);
    } else {
        const pubnub_string_view_t err = pubnub_response_error_message(future);
        printf("Time request failed: %.*s\n", (int)err.len, err.ptr);
    }

    pubnub_future_release(future);
    s_done = 1;
}

int main(void)
{
    pubnub_config_t cfg = pubnub_config_defaults();
    cfg.subscribe_key   = "demo";
    cfg.user_id         = "example-time-async";

    pubnub_context_t* ctx = pubnub_create(&cfg);
    if (NULL == ctx) {
        printf("pubnub_create failed\n");
        return 1;
    }

    pubnub_future_t fut = pubnub_time(ctx);

    const pubnub_res_t rc = pubnub_async(fut, on_time_complete, NULL);
    if (PUBNUB_OK != rc) {
        printf("pubnub_async failed\n");
        pubnub_future_release(fut);
        pubnub_destroy(ctx);
        return 1;
    }

    while (!s_done) {
        pubnub_process(ctx);
    }

    pubnub_destroy(ctx);
    return 0;
}
```

No compiled example in the SDK uses `pubnub_await` with `pubnub_time()` specifically. The blocking style works the same as every other feature call: submit with `pubnub_time(ctx)`, then call `pubnub_await(fut)` in place of the polling loop above.

### Returns

```c
pubnub_timetoken_t pubnub_time_result_timetoken(pubnub_future_t future);
```

Returns the server timetoken as a `pubnub_timetoken_t` view on success. If the future is not yet ready, carries an error, or the response did not parse, it returns a zero-initialized view (`{.ptr = NULL, .len = 0}`). Always check `pubnub_future_status(future) == PUBNUB_OK` before reading the result. Do not infer success from a non-`NULL` `.ptr` alone. Call this before `pubnub_future_release`, since the returned view aliases the future's own response buffer.

### Error responses

| Value | Meaning |
| --- | --- |
| `PUBNUB_ERR_INVALID_ARGUMENT` | `ctx` is `NULL`, or the request could not be queued. |
| `PUBNUB_ERR_SERVER` | The server responded with a non-2xx HTTP status. |
| `PUBNUB_ERR_SERIALIZATION` | The response body was empty or did not parse as a timetoken. |

For the full `pubnub_res_t` catalog, see [Status Events](https://www.pubnub.com/docs/sdks/c/status-events.md).

## SDK version

`pubnub_sdk_version(void)` returns the SDK's version as a static string literal, for example `"1.0.0"`. The value comes from the CMake project version configured at build time (`PUBNUB_SDK_VERSION` in the generated `config.h`), not from a runtime lookup. It's a local, compile-time constant, not a PubNub API call.

## Feature detection

`pubnub_has_feature(ctx, feature)`, declared in `pubnub/capabilities.h`, reports whether a given feature is compiled into and active on a context. It returns non-zero if active, `0` if the feature is disabled at compile time or `ctx` is `NULL`. `feature` is a `pubnub_feature_t` value: `PUBNUB_FEATURE_PUBLISH`, `_SUBSCRIBE`, `_PRESENCE`, `_HISTORY`, `_MESSAGE_ACTIONS`, `_SIGNAL`, `_PAM`, `_APP_CONTEXT`, `_FILES`, `_CHANNEL_GROUPS`, `_CRYPTO`, `_PUSH_NOTIFICATIONS`, or `_TIME`. Use it to check at runtime whether the build you're linked against can make a given kind of PubNub call, instead of relying only on the `PUBNUB_ENABLE_*` flag you built with.

```c
int has_presence = pubnub_has_feature(ctx, PUBNUB_FEATURE_PRESENCE);
```

## Futures and async lifecycle

Every feature function that performs network I/O returns a `pubnub_future_t` by value, declared in `#include <pubnub/future.h>`. The functions on this page are the shared vocabulary behind the three consumption styles used throughout this documentation (cooperative polling, blocking `pubnub_await`, callback-driven `pubnub_async`, see [Environment Setup](https://www.pubnub.com/docs/sdks/c/environment-setup.md#calling-patterns)): they are what every one of those styles is actually built from.

```c
bool         pubnub_future_is_ready(pubnub_future_t future);
pubnub_res_t pubnub_future_status(pubnub_future_t future);
void         pubnub_future_release(pubnub_future_t future);
pubnub_res_t pubnub_await(pubnub_future_t future);
pubnub_res_t pubnub_async(pubnub_future_t future, pubnub_async_cb_t callback, void* user_data);
pubnub_res_t pubnub_future_cancel(pubnub_future_t future);
```

| Function | Returns | Description |
| --- | --- | --- |
| `pubnub_future_is_ready(future)` | `bool` | `true` once the future has reached a terminal state (complete, failed, cancelled, or invalid). |
| `pubnub_future_status(future)` | `pubnub_res_t` | `PUBNUB_IN_PROGRESS` while active, otherwise the final result code. Safe to call from multiple threads once the future is terminal. |
| `pubnub_future_release(future)` | `void` | Returns the request-pool slot to the pool. **Must be called exactly once for every future**, including a future in an error state and `PUBNUB_FUTURE_INVALID`. Safe to call from inside a completion callback, and safe to call on a still in-flight future (reclamation is deferred until the operation completes). |
| `pubnub_await(future)` | `pubnub_res_t` | Blocks until `future` completes. On a threaded build this polls `pubnub_future_is_ready` while yielding; on a cooperative build (no background thread) it drives `pubnub_process` internally, so you do not need your own polling loop. Returns the final status code. |
| `pubnub_async(future, callback, user_data)` | `pubnub_res_t` | Registers `callback` to fire exactly once when `future` reaches a terminal state (firing immediately, inline, if it already has). Returns `PUBNUB_ERR_INVALID_ARGUMENT` when `callback` is `NULL` or `future` carries a `NULL` context. `pubnub_async_cb_t` is `void (*)(pubnub_future_t future, pubnub_res_t status, void* user_data)`. |
| `pubnub_future_cancel(future)` | `pubnub_res_t` | Cancels a pending or in-flight request. The completion callback (if any) still fires, with `PUBNUB_ERR_CANCELLED` for a request that was pending. **Does not release the slot** — call `pubnub_future_release` afterward as usual. |

The required lifecycle order is always: issue → wait/poll (or await, or register a callback) → read result → release.

:::note PUBNUB_FUTURE_INVALID: a future that never touched the network
Some feature functions validate their arguments synchronously and, on failure, return the `PUBNUB_FUTURE_INVALID` sentinel instead of a future tied to a real request. No request was ever sent. `pubnub_future_is_ready()` is already `true` for it, and `pubnub_future_status()` reports `PUBNUB_ERR_INVALID_ARGUMENT`. Because both accessors already behave correctly on it, you do not need to compare a future against this sentinel directly. You do still need to call `pubnub_future_release()` on it exactly once, like any other future.
```c
pubnub_future_t fut = pubnub_time(NULL /* invalid ctx */);
if (PUBNUB_ERR_INVALID_ARGUMENT == pubnub_future_status(fut)) {
    /* fut may be PUBNUB_FUTURE_INVALID, or a real request that failed
       validation for another reason -- either way, this check catches it. */
}
pubnub_future_release(fut);
```
:::

### Cooperative polling: pubnub_process return values

`pubnub_process(ctx)`, declared in `#include <pubnub/client.h>`, pumps the context's event loop for one non-blocking tick: dispatching pending requests, driving transport I/O, and observing completions. Every cooperative-polling example on this site calls it in a loop until the future it is waiting on becomes ready.

| Return value | Meaning |
| --- | --- |
| `PUBNUB_OK` | The context is quiescent; no work remains right now. This is a normal, expected return, not a completion signal for any specific future. |
| `PUBNUB_IN_PROGRESS` | Work remains (a request is still in flight or pending). Also a normal, expected return. |
| `PUBNUB_ERR_NOT_INITIALIZED` | `ctx` is not an initialized context. |

`PUBNUB_IN_PROGRESS` is not an error: it is what most calls to `pubnub_process()` return while a request is outstanding. Check `pubnub_future_is_ready(fut)` (or, in a blocking style, let `pubnub_await` do this for you) to know when to stop calling `pubnub_process()`, not the return value of `pubnub_process()` itself.

## Context size

```c
size_t pubnub_context_size(void);
```

`pubnub_context_size(void)`, declared in `#include <pubnub/client.h>`, returns the number of bytes required for a `pubnub_context_t`. Call it to size a buffer for `pubnub_init()`, the caller-provided-memory lifecycle, when the opaque struct's size is not known at compile time:

```c
size_t             sz  = pubnub_context_size();
pubnub_context_t*  ctx = (pubnub_context_t*)malloc(sz);
pubnub_res_t       rc  = pubnub_init(ctx, &cfg);
```

The returned size can vary between build configurations (which features are compiled in, which platform provider is linked). Always call `pubnub_context_size()` at runtime rather than hardcoding a byte count. On a statically or globally declared buffer, pair this with the `PUBNUB_ALIGNAS` macro from `pubnub/pubnub_compat.h` and the `PUBNUB_CONTEXT_SIZE` compile-time upper bound; see [No-heap operation and static context storage](https://www.pubnub.com/docs/sdks/c/environment-setup.md#no-heap-operation-and-static-context-storage) for the full pattern, including why the alignment macro matters on strictly-aligned targets.

## String views

```c
typedef struct pubnub_string_view {
    const char* ptr; /* NOT NUL-terminated */
    size_t      len;
} pubnub_string_view_t;
```

`pubnub_string_view_t`, declared in `#include <pubnub/types.h>`, is the return type of nearly every result accessor in this SDK: message bodies, timetokens, channel names, and every other piece of string data a future's result carries. `pubnub_timetoken_t` is a type alias for the same struct.

The field `ptr` is explicitly **not NUL-terminated**. Reading it with `printf("%s", sv.ptr)`, `strlen(sv.ptr)`, or `strcmp(sv.ptr, "...")` is undefined behavior: it will read past the intended range in the common case where the underlying buffer happens not to have a `'\0'` immediately after the last character. Always carry `len` alongside `ptr`:

```c
printf("%.*s\n", (int)sv.len, sv.ptr);  /* correct */
memcmp(sv.ptr, "expected", sv.len);     /* correct, when lengths are also compared */
```

A view returned by a result accessor aliases memory owned by the future that produced it, and stays valid only until `pubnub_future_release()` is called on that future (or, for a view inside a subscribe listener callback, only for the duration of that callback invocation). Copy the bytes into your own buffer first if you need them afterward.

## Context getters

```c
const char*                      pubnub_get_user_id(const pubnub_context_t* ctx);
const char*                      pubnub_get_auth_token(const pubnub_context_t* ctx);
const char*                      pubnub_get_origin(const pubnub_context_t* ctx);
pubnub_serialization_provider_t* pubnub_serialization(pubnub_context_t* ctx);
```

All four are declared in `#include <pubnub/client.h>` and let you inspect a context's current state at runtime.

| Function | Returns `NULL` when | Lifetime of the returned pointer |
| --- | --- | --- |
| `pubnub_get_user_id(ctx)` | `ctx` is uninitialized. | Owned by the context. Valid until the next `pubnub_set_user_id()` call or context destruction, whichever comes first. |
| `pubnub_get_auth_token(ctx)` | `ctx` is uninitialized, or no token is set. | Owned by the context. Valid until the next `pubnub_set_auth_token()` call or context destruction. |
| `pubnub_get_origin(ctx)` | `ctx` is `NULL` or uninitialized. | Owned by the context. Valid until the next `pubnub_set_origin()` call or context destruction. |
| `pubnub_serialization(ctx)` | `ctx` is `NULL` or uninitialized. | Borrowed vtable pointer, valid until context destruction. |

None of these pointers should be freed by the caller. `pubnub_serialization(ctx)` returns the context's resolved serialization provider vtable; its optional members may be `NULL`, so check the specific function pointer you intend to call (for example `value_as_string`) before invoking it. See [Providers — Serialization](https://www.pubnub.com/docs/sdks/c/api-reference/providers.md#serialization) for the full vtable and which members are optional.

## Proxy and TCP keepalive

`pubnub_proxy_config_t` and `pubnub_tcp_keepalive_config_t` are `pubnub_config_t` fields, not separate operations. Their field reference, defaults, and per-transport support notes, including which proxy types and authentication schemes the default `curl` transport actually honors, live in [Configuration](https://www.pubnub.com/docs/sdks/c/api-reference/configuration.md#proxy-configuration) and [TCP keepalive](https://www.pubnub.com/docs/sdks/c/api-reference/configuration.md#tcp-keepalive). This page does not repeat those tables.

One fact is easy to miss because it is stated separately for each struct there: both are copied into the active transport provider once, at `pubnub_create()`/`pubnub_init()` time, and neither can be changed for a context that already exists. To change either, build a new `pubnub_config_t` and create or initialize a new context.

## DNS servers

`pubnub_set_dns_servers(ctx, primary, secondary)`, declared in `pubnub/client.h`, points the context at custom DNS servers instead of system-discovered ones:

```c
pubnub_res_t pubnub_set_dns_servers(pubnub_context_t* ctx,
                                    const char*        primary,
                                    const char*        secondary);
```

`primary` and `secondary` are each an IPv4 or IPv6 address string (for example `"8.8.8.8"`), or `NULL`. Passing `NULL` for `primary` clears both servers and reverts to system discovery. This is the current replacement for the legacy C-Core SDK's `pubnub_dns_set_primary_server_ipv4()` and its siblings, all of which have no direct counterpart here beyond this single setter.

It's also the runtime setter for the `dns_primary`/`dns_secondary` config fields described in [Configuration](https://www.pubnub.com/docs/sdks/c/api-reference/configuration.md), so you can set both at context creation and change them later with the same call. It's thread-safe, including on multi-core RTOS targets. It returns `PUBNUB_ERR_NOT_SUPPORTED` if the active transport can't honor custom DNS servers, or `PUBNUB_ERR_INVALID_ARGUMENT` if `primary` or `secondary` is non-`NULL` but not a valid address string.

DNS resolution itself is handled internally, and only by the `socket` transport. The `curl` transport delegates resolution to libcurl and the operating system. Beyond `pubnub_set_dns_servers()`, the only way to influence the internal resolver is through compile-time tunables with no runtime setter or public struct: `PUBNUB_CFG_MAX_DNS_RESULTS`, `PUBNUB_CFG_DNS_CACHE_SIZE`, `PUBNUB_CFG_MAX_DNS_SERVERS`, `PUBNUB_CFG_MAX_HOSTNAME_LEN`, `PUBNUB_CFG_DNS_PLATFORM_STATE_SIZE`, and `PUBNUB_CFG_DNS_MAX_TTL_SEC`. See [Environment Setup](https://www.pubnub.com/docs/sdks/c/environment-setup.md) for how compile-time tunables are set.

## No UUID-generation API

No UUID-generation function exists anywhere in this SDK's public headers. If you are migrating from the legacy C-Core SDK, its UUID v1/v3/v4/v5 generators have no counterpart here, and none is planned on this page. Generate a `user_id` with whatever UUID library your platform already provides.

## Terms in this document

* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
* **Timetoken** - A unique identifier for each message that represents the number of 100-nanosecond intervals since January 1, 1970, for example, 16200000000000000.

Last updated at: 2026-09-24T12:25:41.000Z
