Skip to main content
This guide covers the essential patterns for using the Nuclei SDK in your Go applications.

Creating an engine

The first step is to create a Nuclei engine instance:
import nuclei "github.com/projectdiscovery/nuclei/v3/lib"

ne, err := nuclei.NewNucleiEngine()
if err != nil {
	panic(err)
}
defer ne.Close()
Always call defer ne.Close() to ensure proper cleanup of resources.

Loading targets

Targets can be loaded in multiple ways:

From a slice

// Load targets without HTTP probing
ne.LoadTargets([]string{"scanme.sh", "example.com"}, false)

// Load targets with HTTP probing (auto-detect HTTP/HTTPS)
ne.LoadTargets([]string{"scanme.sh", "example.com"}, true)

From a reader

import (
	"os"
	nuclei "github.com/projectdiscovery/nuclei/v3/lib"
)

file, err := os.Open("targets.txt")
if err != nil {
	panic(err)
}
defer file.Close()

ne.LoadTargetsFromReader(file, false)

From HTTP request files

// Load from Burp XML export
err := ne.LoadTargetsWithHttpData("burp-export.xml", "burp")

// Load from OpenAPI specification
err := ne.LoadTargetsWithHttpData("openapi.yaml", "openapi")

// Load from Swagger specification
err := ne.LoadTargetsWithHttpData("swagger.json", "swagger")
LoadTargetsWithHttpData is mutually exclusive with LoadTargets and LoadTargetsFromReader. Use only one method per scan.

Filtering templates

Filter which templates to run using template filters:

By severity

ne, err := nuclei.NewNucleiEngine(
	nuclei.WithTemplateFilters(nuclei.TemplateFilters{
		Severity: "critical,high",
	}),
)

By tags

ne, err := nuclei.NewNucleiEngine(
	nuclei.WithTemplateFilters(nuclei.TemplateFilters{
		Tags: []string{"cve", "rce"},
	}),
)

By protocol type

ne, err := nuclei.NewNucleiEngine(
	nuclei.WithTemplateFilters(nuclei.TemplateFilters{
		ProtocolTypes: "http,dns",
	}),
)

By template IDs

ne, err := nuclei.NewNucleiEngine(
	nuclei.WithTemplateFilters(nuclei.TemplateFilters{
		IDs: []string{"self-signed-ssl", "CVE-2021-44228"},
	}),
)

Executing scans

Basic execution

// Execute with default output (JSON to stdout)
err = ne.ExecuteWithCallback(nil)
if err != nil {
	panic(err)
}

With custom callback

import "github.com/projectdiscovery/nuclei/v3/pkg/output"

err = ne.ExecuteWithCallback(func(event *output.ResultEvent) {
	fmt.Printf("[%s] %s\n", event.TemplateID, event.Host)
})

With context

import "context"

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

err = ne.ExecuteCallbackWithCtx(ctx, func(event *output.ResultEvent) {
	fmt.Printf("Found: %s\n", event.TemplateID)
})

Complete examples

Simple vulnerability scan

package main

import (
	"fmt"
	nuclei "github.com/projectdiscovery/nuclei/v3/lib"
	"github.com/projectdiscovery/nuclei/v3/pkg/output"
)

func main() {
	// Create engine with critical severity filter
	ne, err := nuclei.NewNucleiEngine(
		nuclei.WithTemplateFilters(nuclei.TemplateFilters{
			Severity: "critical",
		}),
	)
	if err != nil {
		panic(err)
	}
	defer ne.Close()

	// Load targets
	ne.LoadTargets([]string{"scanme.sh"}, false)

	// Execute with callback
	err = ne.ExecuteWithCallback(func(event *output.ResultEvent) {
		fmt.Printf("[%s] %s found on %s\n",
			event.Info.Severity,
			event.TemplateID,
			event.Host,
		)
	})

	if err != nil {
		panic(err)
	}
}

Scan with OAST templates

package main

import (
	"context"
	nuclei "github.com/projectdiscovery/nuclei/v3/lib"
)

func main() {
	ne, err := nuclei.NewNucleiEngineCtx(
		context.Background(),
		nuclei.WithTemplateFilters(nuclei.TemplateFilters{
			Tags: []string{"oast"},
		}),
		nuclei.EnableStatsWithOpts(nuclei.StatsOptions{
			MetricServerPort: 6064,
		}),
	)
	if err != nil {
		panic(err)
	}
	defer ne.Close()

	ne.LoadTargets([]string{"http://honey.scanme.sh"}, false)

	err = ne.ExecuteWithCallback(nil)
	if err != nil {
		panic(err)
	}
}

Working with results

The ResultEvent struct contains detailed information about findings:
func processResult(event *output.ResultEvent) {
	// Template information
	fmt.Println("Template ID:", event.TemplateID)
	fmt.Println("Template Path:", event.TemplatePath)
	fmt.Println("Template Name:", event.Info.Name)
	
	// Severity and classification
	fmt.Println("Severity:", event.Info.Severity)
	fmt.Println("Tags:", event.Info.Tags)
	
	// Target information
	fmt.Println("Host:", event.Host)
	fmt.Println("Matched:", event.Matched)
	
	// Extracted data
	fmt.Println("Extracted:", event.ExtractedResults)
	
	// Request/Response (if available)
	fmt.Println("Request:", event.Request)
	fmt.Println("Response:", event.Response)
}

Error handling

Handle common errors appropriately:
err := ne.ExecuteWithCallback(callback)
if err != nil {
	switch err {
	case nuclei.ErrNoTemplatesAvailable:
		fmt.Println("No templates matched the filters")
	case nuclei.ErrNoTargetsAvailable:
		fmt.Println("No targets were loaded")
	default:
		fmt.Printf("Execution error: %v\n", err)
	}
}

Common patterns

Scanning multiple targets

targets := []string{
	"example.com",
	"test.com",
	"demo.com",
}

ne.LoadTargets(targets, false)
err = ne.ExecuteWithCallback(processResults)

Combining multiple filters

ne, err := nuclei.NewNucleiEngine(
	nuclei.WithTemplateFilters(nuclei.TemplateFilters{
		Severity:      "high,critical",
		Tags:          []string{"cve"},
		ProtocolTypes: "http",
		ExcludeTags:   []string{"intrusive"},
	}),
)

Collecting all results

var results []*output.ResultEvent

err = ne.ExecuteWithCallback(func(event *output.ResultEvent) {
	results = append(results, event)
})

fmt.Printf("Total findings: %d\n", len(results))

Best practices

Always close the engine with defer ne.Close()
Use context for timeout control
Handle errors from all SDK methods
Use appropriate template filters to reduce scan time
Test with safe targets before production use

Next steps

Advanced usage

Learn advanced patterns and optimizations

Configuration options

Explore all configuration options

Build docs developers (and LLMs) love