Current section

Files

Jump to
secio_ex lib secio_ex full_text_search.ex
Raw

lib/secio_ex/full_text_search.ex

defmodule SecioEx.FullTextSearch do
@fulltext_url "https://api.sec-api.io/full-text-search"
@moduledoc """
A way to do a Full text search of SEC filings and get back a list of them.
"""
@doc """
Performs a full-text search across SEC EDGAR filings.
## Parameters
- query: String containing the search term or phrase
- opts: Keyword list of options
- form_types: List of form types to search (e.g., ["8-K", "10-K"])
- start_date: Start date in "YYYY-MM-DD" format
- end_date: End date in "YYYY-MM-DD" format
- ciks: List of CIK numbers to search
- page: Page number for pagination (default: 1)
- api_key: Your SEC API key
## Examples
iex> SecioEx.FullTextSearch.search("SpaceX",
form_types: ["8-K", "10-Q"],
start_date: "2024-01-01",
end_date: "2024-03-31",
api_key: "your_api_key"
)
{:ok, %{total: %{value: 86, relation: "eq"}, filings: [...]}}
# Search with exact phrase
iex> SecioEx.FullTextSearch.search("\"substantial doubt\"",
form_types: ["10-K"],
api_key: "your_api_key"
)
"""
def search(query, opts \\ []) do
api_key = Keyword.fetch!(opts, :api_key)
payload =
%{
"query" => query
}
|> maybe_add_form_types(Keyword.get(opts, :form_types))
|> maybe_add_dates(
Keyword.get(opts, :start_date),
Keyword.get(opts, :end_date)
)
|> maybe_add_ciks(Keyword.get(opts, :ciks))
|> maybe_add_page(Keyword.get(opts, :page))
Req.post(@fulltext_url,
json: payload,
headers: [{"Authorization", api_key}]
)
|> handle_response()
end
@doc """
Helper function to search for an exact phrase.
## Examples
iex> SecioEx.FullTextSearch.search_exact_phrase("substantial doubt",
api_key: "your_api_key"
)
"""
def search_exact_phrase(phrase, opts \\ []) do
search(~s("#{phrase}"), opts)
end
@doc """
Helper function to search with wildcards.
## Examples
iex> SecioEx.FullTextSearch.search_wildcard("gas",
api_key: "your_api_key"
)
"""
def search_wildcard(term, opts \\ []) do
search("#{term}*", opts)
end
@doc """
Helper function to search with multiple terms using OR.
## Examples
iex> SecioEx.FullTextSearch.search_any_of(["qualified opinion", "except for"],
api_key: "your_api_key"
)
"""
def search_any_of(terms, opts \\ []) when is_list(terms) do
query =
terms
|> Enum.map(&~s("#{&1}"))
|> Enum.join(" OR ")
search(query, opts)
end
# Private Functions
defp maybe_add_form_types(payload, nil), do: payload
defp maybe_add_form_types(payload, form_types) when is_list(form_types) do
Map.put(payload, "formTypes", form_types)
end
defp maybe_add_dates(payload, nil, nil), do: payload
defp maybe_add_dates(payload, start_date, end_date) do
payload
|> maybe_put("startDate", start_date)
|> maybe_put("endDate", end_date)
end
defp maybe_add_ciks(payload, nil), do: payload
defp maybe_add_ciks(payload, ciks) when is_list(ciks) do
Map.put(payload, "ciks", ciks)
end
defp maybe_add_page(payload, nil), do: payload
defp maybe_add_page(payload, page) when is_integer(page) do
Map.put(payload, "page", to_string(page))
end
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
defp handle_response({:ok, %Req.Response{status: 200, body: body}}) do
{:ok, body}
end
defp handle_response({:ok, %Req.Response{status: status, body: body}}) do
{:error, %{status_code: status, body: body}}
end
defp handle_response({:error, error}) do
{:error, error}
end
end