DetectProducts
Will detect installed products for a given product category on the endpoint.
Because a single product can come in many versions, installations and other variations, most products will have multiple unique definitions. The 'id' refers to this definition of the product, and is used in future calls to the API to further manipulate or query information about the product.
Syntax
Method ID
#define WAAPI_MID_DETECT_PRODUCTS 0
JSON Input Structure
{
"input": {
"method": 0,
"category": number,
"categories": [
number,
...
],
"single_threaded": boolean,
"verify_product_digital_signature": boolean,
"show_uninstall_hive": boolean,
"show_bundle_info": boolean,
"detect_all_users_products": boolean,
"enable_winget_source": boolean,
"timeout": number
}
}
Input Properties
| Parameter | Type | Description |
|---|---|---|
category | number | (optional) The product category to detect products for. Must be one of the following product categories. If the key does not exist then detection will occur for all products, but the "categories" key will not exist on each entry in the detected products result. This option may provide faster results as we don't perform any category validation. **Only one, 'category' OR 'categories' should be provided in ** json_in. |
categories | array<number> | (optional) A list of product categories from which to detect products. The product must belong to one of these product categories to be detected. If the array is empty or no category key exists then detection will occur for all products, but the "categories" key will not exist on each entry in the detected products result. This option may provide faster results as we don't perform any category validation. **Only one, 'category' OR 'categories' should be provided in ** json_in. |
single_threaded | boolean | (optional) If true, the detection operation will be performed within the invoke thread. This could negatively affect performance, but limits CPU resources. If false, detection will be performed with 3 additional threads. This can enhance the timing of the method but will increase the CPU resources. Default and recommended value is true. |
verify_product_digital_signature | boolean | (optional) If true, each detected product will have its digital certificate/signature verified. |
show_uninstall_hive | boolean | (optional) (Windows only) (Windows only) If true, information about uninstall hive registry will be shown. Default value is 'false'. |
show_bundle_info | boolean | (optional) (macOS only) (macOS only) If true, information about product bundle fields will be shown. Default value is 'false'. |
detect_all_users_products | boolean | (optional) (Windows and macOS) If true, the detection operation will attempt to detect product instances for every local user on the endpoint, not just the currently active user. The default value is false. Requires the calling process to be elevated on both Windows and macOS; if it is not, WAAPI_ERROR_ACCESS_DENIED is returned. Not supported on other platforms (WAAPI_ERROR_COMPONENT_METHOD_NOT_SUPPORTED is returned). (On Windows) Enabling this feature also changes the output shape: only one signature (system-preferred) appears in the result, and the instances key contains details about detected instances (per-system and per-user) as outlined in the next section. Additionally, other features in this method return empty or their default values to avoid conflicts between instances among users. For example, uninstall hive information will return an empty array when the show_uninstall_hive flag is true. Enabling this feature also adds two retryable/actionable return codes to this method - see the return codes section below.(On macOS) The application bundle search additionally scans every local user's home directory instead of only the active console user's, so instances that exist only under a non-active user's home directory are also detected and reported under detected_products.instances. Unlike on Windows, this does not change the output shape of a detected product (signature, bundle_info, etc. are unaffected). |
enable_winget_source | boolean | (optional) (Windows only) (Windows only) If true, the engine also enumerates winget-sourced products alongside OPSWAT-curated products. Each detected product is annotated with a 'data_source' field. Default value is false. Silently ignored on non-Windows platforms |
timeout | number | (optional) (macOS only) (macOS only) Timeout for the detection operation in milliseconds. A value of 0 or less means no timeout (default behavior). If an invalid value is provided, the SDK will ignore this parameter and proceed normally. |
JSON Output Structure
{
"result": {
"method": 0,
"code": number,
"timing": number,
"timestamp": string,
"detected_products": [
{
"signature": number,
"sig_name": string,
"display_name": string,
"categories": [
number,
...
],
"signed": string,
"data_source": string,
"instances": {
"{instance_id}": string
},
"uninstall_hive": [
{
"KeyName": string,
"DisplayName": string,
"DisplayVersion": string,
"InstallLocation": string,
"UninstallString": string,
"InstallDate": string,
"ModifyPath": string,
"Publisher": string
},
...
],
"bundle_info": {
"CFBundleShortVersionString": string,
"Publisher": string
},
"labels": {
"pua": boolean,
"toolbar": boolean,
"bloatware": boolean,
"adware": boolean
},
"package_family_name": string,
"product": {
"id": number,
"name": string
},
"vendor": {
"id": number,
"name": string
}
},
...
]
}
}
Output Properties
| Property | Type | Description |
|---|---|---|
result.method | number | The method ID that generated this result. |
result.code | number | The result code. See Result Codes. |
result.timing | number | The measured time for the method call in milliseconds. |
result.timestamp | string | The timestamp in seconds of when the call was made. |
result.detected_products | array<object> | A list of detected products. If no products are detected for the given category then this will be an empty array. |
result.detected_products.signature | number | The unique identifier for a product signature. |
result.detected_products.sig_name | string | The name collected for this signature of the product. |
result.detected_products.display_name | string | (optional) (Windows only) The display name of the product as it appears in the operating system's list of installed applications (e.g., 'Programs and Features' on Windows). If the name cannot be determined, the value is "n/a". |
result.detected_products.categories | array<number> | The product categories identified for this signature of the product. |
result.detected_products.product.id | number | The unique identifier for the product associated with this signature. |
result.detected_products.product.name | string | The product name. |
result.detected_products.vendor.id | number | The unique identifier for the vendor of this product. |
result.detected_products.vendor.name | string | The vendor name. |
result.detected_products.signed | string | (optional) Digital certificate/signature status. |
result.detected_products.data_source | string | Indicates the data source that produced this product detection. |
result.detected_products.instances | object | (optional) (On Windows) This key appears only if detect_all_users_products is specified. The result will contain a single signature (system-preferred), not both per-system and per-user signatures, and this key will detail the detected instances. (On macOS) All the instances detected for a product. Will appear only if at least one instance is found. (On Linux) This field contains all detected instances of a product installed via supported package managers (DPKG, RPM, SNAP, PACMAN). Each instance represents a distinct installation of the product. This field appears only when at least one instance is detected. |
result.detected_products.instances.{instance_id} | string | (optional) (On Windows) Details about detected instances use the instance ID as the key. The value can be System Wide Instance or the user owning the instance. If the product is installed system-wide, the record includes a system entry, for example, \{ "8edd6b29b9c9c01294202c096ddb2b26": "System Wide Instance" \}. If the product is installed for a specific user, the value contains the domain and username in the format DOMAIN\samAccountName. For example, \{ "8ca1b94936ddbfcaa554ab2aead2aeca" : "LOCAL\test" \}.(On macOS) Full path of the .app file for the corresponding instance id. E.g., \{ "555ef73649a949b2d6d8e9a3d6de9279" : "Users\user1\Desktop\OEAT.app", "74f3200f47e49ade30772fdc4448f4c3": "Users\user1\Downloads\OEAT.app"\}.(On Linux) Details about detected instances use the instance ID as the key. The value is the installation path of the instance on the system. For example: \{ "5e172dff21f5e4fe59e814a7d06e8a7a": "/snap/bin/ruby", "9bbb88008e0beb43d4a0f2fced0b512f": "/usr/bin/ruby" \}. |
result.detected_products.uninstall_hive | array<object> | (optional) (Windows only) Uninstall hive registry locations and content. A product can have multiple locations |
result.detected_products.uninstall_hive.KeyName | string | (optional) (Windows only) Name of the uninstall hive registry key. |
result.detected_products.uninstall_hive.DisplayName | string | (optional) (Windows only) Uninstall hive registry 'DisplayName' value. |
result.detected_products.uninstall_hive.DisplayVersion | string | (optional) (Windows only) Uninstall hive registry 'DisplayVersion' value. |
result.detected_products.uninstall_hive.InstallLocation | string | (optional) (Windows only) Uninstall hive registry 'InstallLocation' value. |
result.detected_products.uninstall_hive.UninstallString | string | (optional) (Windows only) Uninstall hive registry 'UninstallString' value. |
result.detected_products.uninstall_hive.InstallDate | string | (optional) (Windows only) Uninstall hive registry 'InstallDate' value. |
result.detected_products.uninstall_hive.ModifyPath | string | (optional) (Windows only) Uninstall hive registry 'ModifyPath' value. |
result.detected_products.uninstall_hive.Publisher | string | (optional) (Windows only) Uninstall hive registry 'Publisher' value. |
result.detected_products.bundle_info | object | (optional) (macOS only) shows bundle fields |
result.detected_products.bundle_info.CFBundleShortVersionString | string | (optional) (macOS only) CFBundleShortVersionString value. |
result.detected_products.bundle_info.Publisher | string | (optional) (macOS only) Current publisher of the product |
result.detected_products.labels | object | The labels for the product. Defined labels will ALWAYS exist, but additional labels may also be present. |
result.detected_products.labels.pua | boolean | Boolean value which is true if the product is classified as a potentially unwanted application. |
result.detected_products.labels.toolbar | boolean | Boolean value which is true if the product is classified as a toolbar application. |
result.detected_products.labels.bloatware | boolean | Boolean value which is true if the product is classified as a bloatware application. |
result.detected_products.labels.adware | boolean | Boolean value which is true if the product is classified as an adware application. |
result.detected_products.package_family_name | string | (optional) (Windows only) The package family name for Universal Windows Platform (UWP) packages. |
Result Codes
The following codes are the most common return values for this method.
| Code | Constant | Description |
|---|---|---|
| 0 | WAAPI_OK | The operation completed successfully. |
| -5 | WAAPI_ERROR_NOT_INITIALIZED | This method was called while the SDK is not initialized. Call wa_api_setup and verify it returned a success code (use the WAAPI_SUCCESS macro) before invoking this method, and do not invoke it after wa_api_teardown. |
| -11 | WAAPI_ERROR_COMPONENT_METHOD_NOT_SUPPORTED | One or more input parameters are not supported on the current platform. For example, this error may occur if the method is called with detect_all_users_products: true on macOS or Linux, where this flag is not supported. See Platform support for more details. |
| -13 | WAAPI_ERROR_DATABASE_MISSING | The offline database ( libwaresource ) is missing. |
| -14 | WAAPI_ERROR_DATABASE_CORRUPTED | The offline database ( libwaresource ) exists but is corrupted. Perhaps the database is out of date? |
| -15 | WAAPI_ERROR_DATABASE_ENTRY_MISSING | The offline database ( libwaresource ) exists but the entry is missing. |
| -22 | WAAPI_ERROR_ACCESS_DENIED | The process using SDK cannot access one or more system resources required to complete product detection. Please ensure the appropriate permissions. For example, on Windows, running as an administrator is required when detect_all_users_products is set to true. |
| -69 | WAAPI_ERROR_MEMORY_CORUPTED | The memory has been corrupted. Please restart the process to recover the SDK. |
Other codes may be returned. See the full Result Codes reference for the complete list.
Remarks
Error Handling
The results of this function are determined by the error code returned by the call. If successful, WAAPI_OK or a special success code will be returned. Always check both the function return value and the result.code field in the output JSON.
Performance Considerations
The single_threaded parameter provides control over CPU resource usage. For better performance on multi-core systems, consider using multi-threaded detection (set to false).
Platform-Specific Features
| Feature | Windows | macOS | Linux |
|---|---|---|---|
| Basic Detection | ✅ | ✅ | ✅ |
| Uninstall Hive | ✅ | ❌ | ❌ |
| Bundle Info | ❌ | ✅ | ❌ |
| Detect all-users Products | ✅ | ✅ | ❌ |
| Digital Signature | ✅ | ✅ | ❌ |
Configuration Precedence
(macOS only) The restrict_bundle_search option in the global configuration has higher priority than the input.detect_all_users_products request parameter, and overwrites it for any excluded location. If restrict_bundle_search excludes user_home (or all_predefined_values), no account's home directory is scanned for application bundles - even when input.detect_all_users_products is true and detection is requested for every local user. The exclusion is never silently overridden by the broader request, so a product that exists only under an excluded directory is not reported. See Restrict Bundle Search in the Quick Start guide for the full list of accepted values.
Instance Identifiers in the Detection Result
A product can be installed more than once instance on the same endpoint. DetectProducts reports those installations under detected_products.instances, keyed by instance id - but only for the cases described in the Output Properties section: application bundles on macOS, package-manager installs on Linux, and all-users detection on Windows.
Products outside those cases do not get instance ids. When the product is a framework or interpreter - Python, for example - rather than an application bundle, DetectProducts reports it as a single detected entry and does not return an instance_id for it, so the detection result carries no identifier that a later call could use to target a particular installation.
Default Instance Selection at Run Time
Per-product methods that act on an installed product - GetVersion, GetRunningState, Run, TerminateProcesses, GetInstallationDirectories - accept an optional instance_id input. When instance_id is omitted (including when the detection result did not provide one), the SDK resolves a default instance at run time, at the moment that call executes and independently of the earlier DetectProducts result.
The consequence is that the default instance behavior differs at run time: the installation a follow-up call operates on is not guaranteed to be the one DetectProducts reported, and it may change between calls if the set of installed instances on the endpoint changes. Pass an explicit instance_id whenever a specific installation must be targeted; for products that do not report instances, the default resolution is the only behavior available.
Digital Signature Verification with All-Users Detection
(Windows and macOS) input.verify_product_digital_signature and input.detect_all_users_products rely on different kinds of evidence and do not combine. All-users detection is evidence-based detection that aggregates a product across every local user's install evidence - registry hives on Windows, per-user application bundles on macOS. Signature verification is a file-on-disk operation: it must open one installed binary and check its code signature (Authenticode on Windows, code signing on macOS).
When a product is reported as an aggregate across many users, there is no single installed binary to check. Signature verification therefore short-circuits and detected_products.signed is returned empty, rather than returning a misleading answer derived from an arbitrary instance. To obtain a populated signed field, request verify_product_digital_signature without detect_all_users_products - that is, run detection for the active user.
Knowledge Base
(Windows only) If the detect_all_users_products flag is enabled, WAAPI_ERROR_LOGGED_OUT_USER_REGISTRY_CORRUPTED may be returned if the registry for a logged-out user is corrupted and the SDK cannot access it to collect information about installed products. To resolve this, the affected user must log in to Windows interactively (via the Windows sign-in screen) to trigger a registry repair. After that, DetectProducts can retrieve all products installed for all users normally. A profile whose ProfileList entry survives but whose NTUSER.DAT or UsrClass.dat is absent (a stale/orphaned profile) is skipped: DetectProducts succeeds and that user's per-user instances are omitted from detected_products.
Additionally, when detect_all_users_products is enabled, WAAPI_ERROR_RESOURCE_BUSY may be returned - retry the call once the sign-in settles. Why this code is returned: a user was still signing in to Windows at the moment of the scan. DetectProducts never copies a registry hive that the OS is currently mounting, because that would collide with the sign-in and push the user onto a temporary profile; instead it waits briefly for the sign-in to finish and then reads that user's data live. If the sign-in has not completed within that short window, this code is returned so the caller can retry. Note that the call returns no products at all in this case - the result is all-or-nothing, so a single still-signing-in user causes the whole call to fail rather than returning a partial list; the retry is what recovers the complete result. Reading a signing-in user's data requires the SDK to run with administrator or LocalSystem privileges. It is not self-healing for a sign-in that never completes (for example a stuck roaming profile) - retry after the sign-in resolves, or disable detect_all_users_products. The number of profiles still being prepared is reported in error_message.
Note the difference between a profile that is absent and one that is unreadable, because they look alike from outside: a stale/orphaned profile (described above) is skipped and the call still succeeds, but a profile whose hive files exist and cannot be read fails the call rather than being skipped, so that an incomplete product list is never reported as a complete one. This covers a profile denied by permissions, one on a volume that is no longer attached, one whose path is redirected through a junction or sits on a volume with no drive letter, one on a volume that does not support the cooperative-copy mechanism, and one whose registry hive is oversized. These return WAAPI_ERROR_NATIVE_API and retrying does not help - the reason is reported in error_message, and the affected profiles are named in the SDK debug log. A hive the operating system is currently mounting (a sign-in in progress) is different: that is transient and comes back as WAAPI_ERROR_RESOURCE_BUSY, which is worth retrying. A hive merely held open by another process - an anti-malware product or the search indexer - does not block the read at all and does not produce an error.
Examples
Example 1: Detect Antivirus Products
Input:
{
"input": {
"method": 0,
"category": 5
}
}
Output:
{
"result": {
"detected_products": [
{
"display_name": "n/a",
"vendor": {
"name": "Microsoft Corporation",
"id": 90
},
"categories": [
15,
5
],
"signature": 3194,
"product": {
"name": "Microsoft Defender ATP",
"id": 3082
},
"sig_name": "Microsoft Defender ATP",
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false,
"edrxdr": true
}
},
{
"categories": [
5
],
"vendor": {
"name": "Microsoft Corporation",
"id": 90
},
"display_name": "n/a",
"signature": 477,
"product": {
"name": "Windows Defender",
"id": 362
},
"sig_name": "Windows Defender",
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false
}
}
],
"method": 0,
"code": 0,
"timing": 469,
"timestamp": "1773299109"
}
}
Example 2: Multi-User Detection (Windows)
The detect_all_users_products parameter requires administrator permissions.
Input:
{
"input": {
"method": 0,
"single_threaded": true,
"detect_all_users_products": true
}
}
Output:
{
"result": {
"detected_products": [
{
"display_name": "n/a",
"vendor": {
"name": "Microsoft Corporation",
"id": 90
},
"categories": [
10
],
"signature": 3606,
"product": {
"name": "Microsoft Publisher",
"id": 1137
},
"sig_name": "Microsoft Publisher (C2R)",
"instances": {
"8edd6b29b9c9c01294202c096ddb2b26": "System Wide Instance"
},
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false,
"productivity": true,
"commercial": true
}
},
...
{
"vendor": {
"name": "Python Software Foundation",
"id": 555
},
"categories": [
10
],
"display_name": "Python 3.13.12 Tcl/Tk Support (64-bit)",
"signature": 3893,
"product": {
"name": "Python 3.13",
"id": 3527
},
"sig_name": "Python 3.13 64-bit",
"instances": {
"b8b3a8e839a17007d158f7c1b4fa10f7": "OPSWAT\\hoa.nguyen"
},
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false
}
},
{
"vendor": {
"name": "Microsoft Corporation",
"id": 90
},
"categories": [
2,
9
],
"display_name": "Microsoft OneDrive",
"signature": 842,
"product": {
"name": "Microsoft OneDrive",
"id": 833
},
"sig_name": "Microsoft OneDrive",
"instances": {
"b8b3a8e839a17007d158f7c1b4fa10f7": "OPSWAT\\hoa.nguyen",
"999d956a1dfb13a62179921899880f32": "LP11-D8039\\opswat",
"60a7b2d04deb49d687ed693723927faf": "LP11-D8039\\hoa"
},
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false
}
}
],
"method": 0,
"code": 0,
"timing": 4391,
"timestamp": "1775935377"
}
}
Example 3: macOS with Bundle Info
Input:
{
"input": {
"method": 0,
"show_bundle_info": true
}
}
Output:
{
"result": {
"method": 0,
"code": 0,
"timing": 320,
"timestamp": "1707782400",
"detected_products": [
{
"signature": 100190,
"sig_name": "Safari",
"categories": [4, 6],
"product": {
"id": 100013,
"name": "Safari"
},
"vendor": {
"id": 100011,
"name": "Apple Inc."
},
"instances": {
"555ef73649a949b2d6d8e9a3d6de9279": "/Applications/Safari.app/"
},
"bundle_info": {
"CFBundleShortVersionString": "17.2",
"Publisher": "Apple Inc."
},
"labels": {
"pua": false,
"toolbar": false,
"bloatware": false,
"adware": false
}
}
]
}
}
Code Example (C++)
The following self-contained example sets up the SDK, invokes DetectProducts with the simplest possible input ({"input":{"method":0}} — detect across all categories), prints the raw json_out, and call the SDK teardown.
#include <iostream>
#include <string>
#include "wa_api.h"
int main()
{
// ---------------------------------------------------------------
// Step 1: Setup the SDK
// ---------------------------------------------------------------
const std::wstring json_config =
L"{ \"config\": { \"passkey_string\": \"Your-Passkey-String\" } }";
wa_wchar* setup_out = nullptr;
int rc = wa_api_setup(json_config.c_str(), &setup_out);
if (!WAAPI_SUCCESS(rc))
{
std::wcerr << L"wa_api_setup failed. Code: " << rc << std::endl;
if (setup_out) wa_api_free(setup_out);
return 1;
}
if (setup_out) wa_api_free(setup_out);
// ---------------------------------------------------------------
// Step 2: Invoke DetectProducts (method = 0)
// Equivalent to json_in = {"input":{"method":0}}
// ---------------------------------------------------------------
const std::wstring json_in = L"{ \"input\": { \"method\": 0 } }";
wa_wchar* json_out = nullptr;
rc = wa_api_invoke(json_in.c_str(), &json_out);
if (WAAPI_SUCCESS(rc) && json_out != nullptr)
{
std::wcout << L"DetectProducts succeeded:" << std::endl;
std::wcout << json_out << std::endl;
// Example json_out payload:
// {
// "result": {
// "method": 0,
// "code": 0,
// "timing": 469,
// "timestamp": "1773299109",
// "detected_products": [
// {
// "signature": 477,
// "sig_name": "Windows Defender",
// "categories": [5],
// "product": { "id": 362, "name": "Windows Defender" },
// "vendor": { "id": 90, "name": "Microsoft Corporation" }
// }
// ]
// }
// }
}
else
{
std::wcerr << L"wa_api_invoke failed. Code: " << rc << std::endl;
}
// Important: caller owns json_out — free it after use
if (json_out) wa_api_free(json_out);
// ---------------------------------------------------------------
// Step 3: Teardown the SDK
// ---------------------------------------------------------------
wa_api_teardown();
return 0;
}
See Also
Last updated: 2026-10-01