> ## Documentation Index
> Fetch the complete documentation index at: https://docs.theswarm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Query Tester

> Test your OpenSearch DSL queries against the Swarm API and see real results.

export const SwarmQueryTester = () => {
  const executeUrl = "https://theswarm-query-builder.vercel.app/api/execute";
  const [apiKey, setApiKey] = useState("");
  const [showApiKey, setShowApiKey] = useState(false);
  const [query, setQuery] = useState("");
  const [queryType, setQueryType] = useState("profiles");
  const [maxResults, setMaxResults] = useState(20);
  const [result, setResult] = useState("");
  const [loading, setLoading] = useState(false);
  const [paginationToken, setPaginationToken] = useState(null);
  const [totalCount, setTotalCount] = useState(null);
  const [copied, setCopied] = useState(false);
  useEffect(() => {
    const savedApiKey = localStorage.getItem("swarm-api-key");
    if (savedApiKey) {
      setApiKey(savedApiKey);
    }
  }, []);
  const handleApiKeyChange = e => {
    const newApiKey = e.target.value;
    setApiKey(newApiKey);
    if (newApiKey.trim()) {
      localStorage.setItem("swarm-api-key", newApiKey);
    } else {
      localStorage.removeItem("swarm-api-key");
    }
  };
  const clearApiKey = () => {
    setApiKey("");
    localStorage.removeItem("swarm-api-key");
  };
  const runQuery = async (parsedQuery, token) => {
    const response = await fetch(executeUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        query: parsedQuery,
        type: queryType,
        apiKey: apiKey.trim(),
        limit: maxResults,
        paginationToken: token || null
      })
    });
    const data = await response.json().catch(() => null);
    if (response.ok && data?.success) {
      setResult(JSON.stringify(data.data, null, 2));
      setTotalCount(data.data?.total_count ?? null);
      setPaginationToken(data.data?.pagination_token || null);
    } else {
      setResult(`Error: ${data?.error || `Request failed (status ${response.status})`}`);
    }
  };
  const testQuery = async () => {
    if (!apiKey.trim() || !query.trim()) return;
    setLoading(true);
    setResult("");
    setTotalCount(null);
    setPaginationToken(null);
    let parsedQuery;
    try {
      parsedQuery = JSON.parse(query);
    } catch (parseError) {
      setResult(`Error: Invalid JSON query format - ${parseError.message}`);
      setLoading(false);
      return;
    }
    try {
      await runQuery(parsedQuery, null);
    } catch (error) {
      console.error("Execute failed:", error);
      setResult(`Error: ${error?.message || "Failed to connect to server"}`);
    } finally {
      setLoading(false);
    }
  };
  const loadNextPage = async () => {
    if (!paginationToken) return;
    let parsedQuery;
    try {
      parsedQuery = JSON.parse(query);
    } catch (parseError) {
      setResult(`Error: Invalid JSON query format - ${parseError.message}`);
      return;
    }
    setLoading(true);
    try {
      await runQuery(parsedQuery, paginationToken);
    } catch (error) {
      console.error("Execute failed:", error);
      setResult(`Error: ${error?.message || "Failed to connect to server"}`);
    } finally {
      setLoading(false);
    }
  };
  const resetPagination = () => {
    setPaginationToken(null);
    setResult("");
  };
  const copyToClipboard = async () => {
    if (!result) return;
    try {
      await navigator.clipboard.writeText(result);
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    } catch (error) {
      console.error("Failed to copy to clipboard:", error);
    }
  };
  const downloadResults = () => {
    if (!result || result.startsWith("Error:")) return;
    try {
      const data = JSON.parse(result);
      const dataStr = JSON.stringify(data, null, 2);
      const dataBlob = new Blob([dataStr], {
        type: "application/json"
      });
      const url = URL.createObjectURL(dataBlob);
      const link = document.createElement("a");
      link.href = url;
      link.download = `swarm-query-results-${Date.now()}.json`;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error("Failed to download results:", error);
    }
  };
  const handleQueryChange = e => {
    setQuery(e.target.value);
    if (paginationToken) {
      setPaginationToken(null);
    }
  };
  const renderJSON = text => {
    if (!text) return null;
    if (text.startsWith("Error:")) {
      return <div className="json-error">{text}</div>;
    }
    try {
      const jsonObj = JSON.parse(text);
      const renderValue = (value, indent = 0) => {
        const spaces = ("  ").repeat(indent);
        if (value === null) {
          return <span className="json-null">null</span>;
        }
        if (typeof value === "boolean") {
          return <span className="json-boolean">{String(value)}</span>;
        }
        if (typeof value === "number") {
          return <span className="json-number">{value}</span>;
        }
        if (typeof value === "string") {
          return <span className="json-string">"{value}"</span>;
        }
        if (Array.isArray(value)) {
          if (value.length === 0) {
            return <>
                <span className="json-punctuation">[</span>
                <span className="json-punctuation">]</span>
              </>;
          }
          return <>
              <span className="json-punctuation">[</span>
              <div style={{
            marginLeft: "20px"
          }}>
                {value.map((item, index) => <div key={index}>
                    {spaces}
                    {renderValue(item, indent + 1)}
                    {index < value.length - 1 && <span className="json-punctuation">,</span>}
                  </div>)}
              </div>
              {spaces}<span className="json-punctuation">]</span>
            </>;
        }
        if (typeof value === "object") {
          const keys = Object.keys(value);
          if (keys.length === 0) {
            return <>
                <span className="json-punctuation">{`{`}</span>
                <span className="json-punctuation">{`}`}</span>
              </>;
          }
          return <>
              <span className="json-punctuation">{`{`}</span>
              <div style={{
            marginLeft: "20px"
          }}>
                {keys.map((key, index) => <div key={key}>
                    {spaces}
                    <span className="json-key">"{key}"</span>
                    <span className="json-punctuation">: </span>
                    {renderValue(value[key], indent + 1)}
                    {index < keys.length - 1 && <span className="json-punctuation">,</span>}
                  </div>)}
              </div>
              {spaces}<span className="json-punctuation">{`}`}</span>
            </>;
        }
        return String(value);
      };
      return renderValue(jsonObj);
    } catch (e) {
      return <div>{text}</div>;
    }
  };
  return <div style={{
    padding: "12px 24px 24px",
    border: "1px solid rgba(255, 255, 255, 0.1)",
    borderRadius: "12px",
    margin: "20px 0",
    background: "rgba(255, 255, 255, 0.05)",
    color: "#f9fafb",
    fontFamily: "Roobert Pro, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif"
  }}>

      {}
      <div style={{
    marginBottom: "16px"
  }}>
        <div style={{
    display: "flex",
    background: "transparent",
    borderRadius: "0",
    padding: "0",
    gap: "0"
  }}>
          <button onClick={() => setQueryType("profiles")} style={{
    flex: "none",
    padding: "12px 20px",
    border: "none",
    borderRadius: "0",
    background: "transparent",
    color: queryType === "profiles" ? "rgb(255, 157, 87)" : "rgba(255,255,255,0.9)",
    cursor: "pointer",
    fontSize: "14px",
    fontWeight: "500",
    borderBottom: queryType === "profiles" ? "1px solid rgb(255, 157, 87)" : "none",
    transition: "all 0.2s ease"
  }}>
            Profiles
          </button>
          <button onClick={() => setQueryType("companies")} style={{
    flex: "none",
    padding: "12px 20px",
    border: "none",
    borderRadius: "0",
    background: "transparent",
    color: queryType === "companies" ? "rgb(255, 157, 87)" : "rgba(255,255,255,0.9)",
    cursor: "pointer",
    fontSize: "14px",
    fontWeight: "500",
    borderBottom: queryType === "companies" ? "1px solid rgb(255, 157, 87)" : "none",
    transition: "all 0.2s ease"
  }}>
            Companies
          </button>
        </div>
      </div>

      {}
      <div style={{
    marginBottom: "16px"
  }}>
        <div style={{
    marginBottom: "8px",
    fontSize: "14px",
    fontWeight: "500",
    color: "rgba(255,255,255,0.9)"
  }}>
          Swarm API Key
        </div>
        <div style={{
    position: "relative"
  }}>
          <input type={showApiKey ? "text" : "password"} value={apiKey} onChange={handleApiKeyChange} placeholder="Enter your Swarm API key" style={{
    width: "100%",
    padding: "12px 16px",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "8px",
    background: "rgba(0,0,0,0.5)",
    color: "#f9fafb",
    fontSize: "14px",
    fontFamily: "inherit"
  }} />
          <div style={{
    position: "absolute",
    right: "12px",
    top: "50%",
    transform: "translateY(-50%)",
    display: "flex",
    gap: "8px"
  }}>
            {apiKey && <button onClick={clearApiKey} style={{
    background: "none",
    border: "none",
    color: "rgba(255,255,255,0.6)",
    cursor: "pointer",
    padding: "4px"
  }} title="Clear API key">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                </svg>
              </button>}
            <button onClick={() => setShowApiKey(!showApiKey)} style={{
    background: "none",
    border: "none",
    color: "rgba(255,255,255,0.6)",
    cursor: "pointer",
    padding: "4px"
  }} title={showApiKey ? "Hide API key" : "Show API key"}>
              {showApiKey ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21" />
                </svg> : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
                </svg>}
            </button>
          </div>
        </div>
        <div style={{
    fontSize: "12px",
    color: "rgba(255,255,255,0.6)",
    marginTop: "4px"
  }}>
          Your API key is stored locally in your browser. It is sent only to proxy your request to the Swarm API and is never stored on our servers.
        </div>
      </div>

      {}
      <div style={{
    marginBottom: "16px"
  }}>
        <div style={{
    marginBottom: "8px",
    fontSize: "14px",
    fontWeight: "500",
    color: "rgba(255,255,255,0.9)"
  }}>
          Max Results
        </div>
        <input type="number" min="1" max="100" value={maxResults} onChange={e => setMaxResults(parseInt(e.target.value) || 20)} style={{
    width: "100%",
    padding: "12px 16px",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "8px",
    background: "rgba(0,0,0,0.5)",
    color: "#f9fafb",
    fontSize: "14px",
    fontFamily: "inherit"
  }} />
      </div>

      {}
      <div style={{
    marginBottom: "16px"
  }}>
        <div style={{
    marginBottom: "8px",
    fontSize: "14px",
    fontWeight: "500",
    color: "rgba(255,255,255,0.9)"
  }}>
          OpenSearch DSL Query
        </div>
        <textarea value={query} onChange={handleQueryChange} placeholder="Paste your query here..." style={{
    width: "100%",
    padding: "12px 16px",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "8px",
    background: "rgba(0,0,0,0.5)",
    color: "#f9fafb",
    fontSize: "13px",
    lineHeight: "1.4",
    resize: "vertical",
    minHeight: "120px",
    fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
  }} />
      </div>

      {}
      <button onClick={testQuery} disabled={loading || !apiKey.trim() || !query.trim()} style={{
    width: "100%",
    padding: "12px 24px",
    background: loading || !apiKey.trim() || !query.trim() ? "rgb(55 55 55)" : "rgb(255, 157, 87)",
    color: !loading && apiKey.trim() && query.trim() ? "black" : "white",
    border: "none",
    borderRadius: "8px",
    fontSize: "15px",
    fontWeight: "600",
    cursor: loading || !apiKey.trim() || !query.trim() ? "not-allowed" : "pointer",
    marginBottom: "16px",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    gap: "8px"
  }}>
        {loading ? <>
            <div style={{
    width: "16px",
    height: "16px",
    border: "2px solid transparent",
    borderTop: "2px solid white",
    borderRadius: "50%",
    animation: "spin 1s linear infinite"
  }} />
            Executing Query...
          </> : <>
            Execute Query
          </>}
      </button>

      {}
      {paginationToken && <div style={{
    display: "flex",
    gap: "12px",
    marginBottom: "16px"
  }}>
          <button onClick={loadNextPage} disabled={loading} style={{
    flex: "1",
    padding: "8px 16px",
    background: "rgba(255,255,255,0.1)",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "6px",
    color: "#f9fafb",
    fontSize: "14px",
    cursor: loading ? "not-allowed" : "pointer",
    opacity: loading ? 0.5 : 1
  }}>
            Load Next Page
          </button>
          <button onClick={resetPagination} style={{
    padding: "8px 16px",
    background: "rgba(255,255,255,0.1)",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "6px",
    color: "#f9fafb",
    fontSize: "14px",
    cursor: "pointer"
  }}>
            Reset
          </button>
        </div>}

      {}
      {result && <div style={{
    marginTop: "20px"
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    marginBottom: "12px"
  }}>
            <h4 style={{
    margin: 0,
    fontSize: "16px",
    fontWeight: "600"
  }}>
              {result.startsWith("Error:") ? "Error" : `API Response${totalCount != null ? ` — ${totalCount.toLocaleString()} total matches` : ""}`}
            </h4>
            {!result.startsWith("Error:") && <div style={{
    display: "flex",
    gap: "8px"
  }}>
                <button onClick={copyToClipboard} style={{
    padding: "6px 12px",
    background: copied ? "rgba(255, 140, 0, 0.15)" : "rgba(255,255,255, 0.2)",
    border: copied ? "1px solid orange" : "1px solid rgba(0, 0, 0, 0.9)",
    borderRadius: "6px",
    color: copied ? "#ff9800" : "#d1d5db",
    fontSize: "12px",
    cursor: "pointer",
    display: "flex",
    alignItems: "center",
    gap: "6px",
    transition: "all 0.2s"
  }} disabled={copied}>
                  {copied ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
                    </svg> : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                      <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
                      <rect x="8" y="2" width="8" height="4" rx="1" ry="1" />
                    </svg>}
                  {copied ? "Copied" : "Copy"}
                </button>
                <button onClick={downloadResults} style={{
    padding: "6px 12px",
    background: "rgba(255,255,255, 0.2)",
    border: "1px solid rgba(0, 0, 0, 0.9)",
    borderRadius: "6px",
    color: "#d1d5db",
    fontSize: "12px",
    cursor: "pointer",
    display: "flex",
    alignItems: "center",
    gap: "6px"
  }}>
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
                    <polyline strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} points="7,10 12,15 17,10" />
                    <line strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} x1="12" y1="15" x2="12" y2="3" />
                  </svg>
                  Download
                </button>
              </div>}
          </div>
          <div style={{
    background: "rgba(0,0,0,1.0)",
    border: "1px solid rgba(255,255,255,0.2)",
    borderRadius: "8px",
    padding: "16px",
    fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
    fontSize: "12px",
    lineHeight: "1.4",
    overflow: "auto",
    color: "#f9fafb",
    margin: 0,
    maxHeight: "400px",
    minHeight: "100px"
  }}>
            {renderJSON(result)}
          </div>
        </div>}

      {}
      <div style={{
    marginTop: "16px",
    padding: "12px 16px",
    background: "rgba(0,128,255,0.15)",
    border: "1px solid rgba(0,125,255,.5)",
    borderRadius: "8px",
    fontSize: "12px",
    color: "rgba(255,255,255,0.9)",
    display: "flex",
    alignItems: "flex-start",
    gap: "10px"
  }}>
        <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-label="Info" style={{
    flex: "none",
    width: "20px",
    height: "20px",
    color: "#a1a1aa",
    marginTop: "2px"
  }}>
          <path d="M8 0C3.58125 0 0 3.58125 0 8C0 12.4187 3.58125 16 8 16C12.4187 16 16 12.4187 16 8C16 3.58125 12.4187 0 8 0ZM8 14.5C4.41563 14.5 1.5 11.5841 1.5 8C1.5 4.41594 4.41563 1.5 8 1.5C11.5844 1.5 14.5 4.41594 14.5 8C14.5 11.5841 11.5844 14.5 8 14.5ZM9.25 10.5H8.75V7.75C8.75 7.3375 8.41563 7 8 7H7C6.5875 7 6.25 7.3375 6.25 7.75C6.25 8.1625 6.5875 8.5 7 8.5H7.25V10.5H6.75C6.3375 10.5 6 10.8375 6 11.25C6 11.6625 6.3375 12 6.75 12H9.25C9.66406 12 10 11.6641 10 11.25C10 10.8359 9.66563 10.5 9.25 10.5ZM8 6C8.55219 6 9 5.55219 9 5C9 4.44781 8.55219 4 8 4C7.44781 4 7 4.44687 7 5C7 5.55313 7.44687 6 8 6Z"></path>
        </svg>
        <div>
          <strong>How to use:</strong><br />
          1. Select query type (profiles or companies)<br />
          2. Enter your Swarm API key<br />
          3. Set max results limit<br />
          4. Paste your OpenSearch DSL query<br />
          5. Click Execute to test against real API<br />
        </div>
      </div>

      <style>{`
        @keyframes spin {
          0% { transform: rotate(0deg); }
          100% { transform: rotate(360deg); }
        }

        /* JSON Syntax Highlighting */
        .json-key {
          color: #7dd3fc; /* Light blue/cyan for keys */
        }

        .json-string {
          color: #fbbf24; /* Light orange/peach for strings */
        }

        .json-number {
          color: #86efac; /* Light green for numbers */
        }

        .json-boolean {
          color: #c084fc; /* Purple for booleans */
        }

        .json-null {
          color: #f87171; /* Red for null */
        }

        .json-punctuation {
          color: #ffffff; /* White for punctuation */
        }

        .json-error {
          color: #f87171; /* Red for error messages */
        }
      `}</style>
    </div>;
};

[Build the query](/docs/examples/api-query-builder)
