diff --git a/format/format.go b/format/format.go
index 8bb3877..a6d9648 100644
--- a/format/format.go
+++ b/format/format.go
@@ -1,7 +1,11 @@
 package format
 
 import (
+	"bytes"
+	"encoding/csv"
 	"encoding/json"
+	"fmt"
+	"strings"
 
 	"gopkg.in/yaml.v2"
 )
@@ -22,3 +26,45 @@ func ToYAML(data interface{}) (string, error) {
 	}
 	return string(bytes), nil
 }
+
+func ToCSV(data interface{}, headers []string) (string, error) {
+	jsonObj, ok := data.(map[string]interface{})
+	if !ok {
+		return "", fmt.Errorf("data is not a JSON object")
+	}
+
+	var csvBuffer bytes.Buffer
+	writer := csv.NewWriter(&csvBuffer)
+
+	record := make([]string, len(headers))
+	for i, header := range headers {
+		header = strings.TrimPrefix(header, "/")
+		var value interface{} = jsonObj
+
+		for _, key := range strings.Split(header, ".") {
+			if tempMap, ok := value.(map[string]interface{}); ok {
+				value, ok = tempMap[key]
+				if !ok {
+					value = ""
+					break
+				}
+			} else {
+				break
+			}
+		}
+
+		record[i] = fmt.Sprintf("%v", value)
+	}
+
+	if err := writer.Write(record); err != nil {
+		return "", fmt.Errorf("writing record to CSV failed: %w", err)
+	}
+
+	writer.Flush()
+
+	if err := writer.Error(); err != nil {
+		return "", fmt.Errorf("CSV writing failed: %w", err)
+	}
+
+	return csvBuffer.String(), nil
+}
diff --git a/format/format_test.go b/format/format_test.go
index 6046a4b..e95838e 100644
--- a/format/format_test.go
+++ b/format/format_test.go
@@ -39,3 +39,62 @@ func TestToYAML(t *testing.T) {
 		t.Errorf("Expected YAML string to contain %s, got %s", expectedSubstring, yamlStr)
 	}
 }
+
+func TestToCSV(t *testing.T) {
+	tests := []struct {
+		name    string
+		data    map[string]interface{}
+		headers []string
+		want    string
+	}{
+		{
+			name: "Simple fields",
+			data: map[string]interface{}{
+				"name": "Example",
+				"id":   "123",
+			},
+			headers: []string{"/name", "/id"},
+			want:    "Example,123\n",
+		},
+		{
+			name: "Fields with commas and quotes",
+			data: map[string]interface{}{
+				"description": `Product "A", the best one`,
+				"notes":       "It's, literally, \"awesome\".",
+			},
+			headers: []string{"/description", "/notes"},
+			want:    "\"Product \"\"A\"\", the best one\",\"It's, literally, \"\"awesome\"\".\"\n",
+		},
+		{
+			name: "Nested fields",
+			data: map[string]interface{}{
+				"reported": map[string]interface{}{
+					"location": "Warehouse, 42",
+					"status":   "In-stock",
+				},
+			},
+			headers: []string{"/reported.location", "/reported.status"},
+			want:    "\"Warehouse, 42\",In-stock\n",
+		},
+		{
+			name: "Missing and empty fields",
+			data: map[string]interface{}{
+				"reported": map[string]interface{}{
+					"location": "Remote",
+				},
+			},
+			headers: []string{"/reported.location", "/reported.quantity"},
+			want:    "Remote,\n",
+		},
+	}
+
+	for _, tt := range tests {
+		got, err := ToCSV(tt.data, tt.headers)
+		if err != nil {
+			t.Errorf("TestToCSV %s failed with error: %v", tt.name, err)
+		}
+		if got != tt.want {
+			t.Errorf("TestToCSV %s expected %q, got %q", tt.name, tt.want, got)
+		}
+	}
+}
diff --git a/main.go b/main.go
index b4e5d51..5229151 100644
--- a/main.go
+++ b/main.go
@@ -27,7 +27,8 @@ func main() {
 	searchStrPtr := flag.String("search", "", "Search string")
 	usernamePtr := flag.String("username", "", "Username (env FIX_USERNAME)")
 	passwordPtr := flag.String("password", "", "Password (env FIX_PASSWORD)")
-	formatPtr := flag.String("format", "json", "Output format: json or yaml")
+	formatPtr := flag.String("format", "json", "Output format: json, yaml or csv")
+	csvHeadersPtr := flag.String("csv-headers", "id,name,kind,/ancestors.cloud.reported.id,/ancestors.account.reported.id,/ancestors.region.reported.id", "CSV headers (comma-separated, relative to /reported by default)")
 	withEdgesPtr := flag.Bool("with-edges", false, "Include edges in search results")
 	help := flag.Bool("help", false, "Display help information")
 	flag.Parse()
@@ -37,36 +38,44 @@ func main() {
 	}
 
 	withEdges := *withEdgesPtr
-	outputFormat := *formatPtr
 
+	invalidArgs := false
 	username, password, err := utils.SanitizeCredentials(utils.GetEnvOrDefault("FIX_USERNAME", *usernamePtr), utils.GetEnvOrDefault("FIX_PASSWORD", *passwordPtr))
 	if err != nil {
 		fmt.Println("Invalid username or password:", err)
-		os.Exit(1)
+		invalidArgs = true
 	}
 	searchStr, err := utils.SanitizeSearchString(*searchStrPtr)
 	if err != nil {
 		fmt.Println("Invalid search string:", err)
-		os.Exit(1)
+		invalidArgs = true
 	}
 	apiEndpoint, err := utils.SanitizeAPIEndpoint(utils.GetEnvOrDefault("FIX_ENDPOINT", *apiEndpointPtr))
 	if err != nil {
 		fmt.Println("Invalid API endpoint:", err)
-		os.Exit(1)
+		invalidArgs = true
 	}
 	fixToken, err := utils.SanitizeToken(utils.GetEnvOrDefault("FIX_TOKEN", *fixTokenPtr))
 	if err != nil {
 		fmt.Println("Invalid token:", err)
-		os.Exit(1)
+		invalidArgs = true
 	}
 	workspaceID, err := utils.SanitizeWorkspaceId(utils.GetEnvOrDefault("FIX_WORKSPACE", *workspacePtr))
 	if err != nil {
 		fmt.Println("Invalid workspace ID:", err)
-		os.Exit(1)
+		invalidArgs = true
 	}
-
-	if outputFormat != "json" && outputFormat != "yaml" {
-		fmt.Println("Invalid output format")
+	csvHeaders, err := utils.SanitizeCSVHeaders(*csvHeadersPtr)
+	if err != nil {
+		fmt.Println("Invalid CSV headers:", err)
+		invalidArgs = true
+	}
+	outputFormat, err := utils.SanitizeOutputFormat(*formatPtr)
+	if err != nil {
+		fmt.Println("Invalid output format:", err)
+		invalidArgs = true
+	}
+	if invalidArgs {
 		os.Exit(1)
 	}
 
@@ -96,6 +105,8 @@ func main() {
 				firstResult = false
 			}
 			output, err = format.ToYAML(result)
+		case "csv":
+			output, err = format.ToCSV(result, csvHeaders)
 		default:
 			output, err = format.ToJSON(result)
 		}
diff --git a/utils/utils.go b/utils/utils.go
index 6033be8..1e4b19f 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -72,3 +72,37 @@ func SanitizeWorkspaceId(workspaceId string) (string, error) {
 
 	return workspaceId, nil
 }
+
+func SanitizeCSVHeaders(headers string) ([]string, error) {
+	if headers == "" {
+		return nil, fmt.Errorf("headers cannot be empty")
+	}
+
+	rawHeaders := strings.Split(headers, ",")
+	if len(rawHeaders) == 0 {
+		return nil, fmt.Errorf("at least one header must be specified")
+	}
+
+	csvHeaders := make([]string, len(rawHeaders))
+	for i, header := range rawHeaders {
+		trimmedHeader := strings.TrimSpace(header)
+		if trimmedHeader == "" {
+			return nil, fmt.Errorf("empty CSV header found")
+		}
+
+		if !strings.HasPrefix(trimmedHeader, "/") {
+			trimmedHeader = "/reported." + trimmedHeader
+		}
+		csvHeaders[i] = trimmedHeader
+	}
+	return csvHeaders, nil
+}
+
+func SanitizeOutputFormat(format string) (string, error) {
+	switch format {
+	case "json", "yaml", "csv":
+		return format, nil
+	default:
+		return "", fmt.Errorf("unsupported output format")
+	}
+}
diff --git a/utils/utils_test.go b/utils/utils_test.go
index efab327..7ea9ca5 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -2,6 +2,7 @@ package utils
 
 import (
 	"os"
+	"reflect"
 	"strings"
 	"testing"
 )
@@ -140,3 +141,121 @@ func TestSanitizeWorkspaceId(t *testing.T) {
 		}
 	}
 }
+
+func TestSanitizeOutputFormat(t *testing.T) {
+	tests := []struct {
+		name      string
+		format    string
+		want      string
+		wantError bool
+	}{
+		{
+			name:      "Valid format json",
+			format:    "json",
+			want:      "json",
+			wantError: false,
+		},
+		{
+			name:      "Valid format yaml",
+			format:    "yaml",
+			want:      "yaml",
+			wantError: false,
+		},
+		{
+			name:      "Valid format csv",
+			format:    "csv",
+			want:      "csv",
+			wantError: false,
+		},
+		{
+			name:      "Unsupported format",
+			format:    "xml",
+			want:      "",
+			wantError: true,
+		},
+		{
+			name:      "Empty format",
+			format:    "",
+			want:      "",
+			wantError: true,
+		},
+		{
+			name:      "Whitespace format",
+			format:    " ",
+			want:      "",
+			wantError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		got, err := SanitizeOutputFormat(tt.format)
+		if (err != nil) != tt.wantError {
+			t.Errorf("%s: SanitizeOutputFormat(%s) expected error: %v, got: %v", tt.name, tt.format, tt.wantError, err)
+		}
+		if got != tt.want {
+			t.Errorf("%s: SanitizeOutputFormat(%s) = %v, want %v", tt.name, tt.format, got, tt.want)
+		}
+	}
+}
+
+func TestSanitizeCSVHeaders(t *testing.T) {
+	tests := []struct {
+		name      string
+		headers   string
+		want      []string
+		wantError bool
+	}{
+		{
+			name:      "Non-empty headers without leading slash",
+			headers:   "id,name,kind",
+			want:      []string{"/reported.id", "/reported.name", "/reported.kind"},
+			wantError: false,
+		},
+		{
+			name:      "Headers with leading slash",
+			headers:   "/metadata.expires,/metadata.cleaned",
+			want:      []string{"/metadata.expires", "/metadata.cleaned"},
+			wantError: false,
+		},
+		{
+			name:      "Mixed headers",
+			headers:   "name,/metadata.expires,kind",
+			want:      []string{"/reported.name", "/metadata.expires", "/reported.kind"},
+			wantError: false,
+		},
+		{
+			name:      "Empty headers string",
+			headers:   "",
+			want:      nil,
+			wantError: true,
+		},
+		{
+			name:      "Only whitespace",
+			headers:   "  ",
+			want:      nil,
+			wantError: true,
+		},
+		{
+			name:      "Header with only commas",
+			headers:   ",,,",
+			want:      nil,
+			wantError: true,
+		},
+		{
+			name:      "Empty header among valid headers",
+			headers:   "id,,name",
+			want:      nil,
+			wantError: true,
+		},
+	}
+
+	for _, tt := range tests {
+		got, err := SanitizeCSVHeaders(tt.headers)
+		if (err != nil) != tt.wantError {
+			t.Errorf("%s: SanitizeCSVHeaders() error = %v, wantError %v", tt.name, err, tt.wantError)
+		}
+		if !reflect.DeepEqual(got, tt.want) {
+			t.Errorf("%s: SanitizeCSVHeaders() = %v, want %v", tt.name, got, tt.want)
+		}
+	}
+}