Harbour API v1.0.0

Harbour is the API gateway for Equity Data Science (EDS). It provides rate-limited, authenticated access to portfolio analytics, Excel/Outlook add-in services, OAuth token exchange, and the MCP protocol.

Base URL: https://harbour-api.equitydatascience.com

28 endpoints across 3 categories

Authentication

eds-api-key — Header: eds-api-key

API key issued per user. Passed in the 'eds-api-key' request header.

jwt-bearer

OAuth 2.0 JWT bearer token. Passed in the 'Authorization: Bearer ' header. Used exclusively by MCP protocol endpoints.

Not authorized

Authorize with your API key above to view API endpoint documentation.

Rate Limiting

Rate limiting is enforced per-user across all endpoints. Limits apply across three time windows: per-minute, per-hour, and per-day.

Default limits: 5,000/min, 30,000/hr, 60,000/day

Error Codes

CodeNameDescription
400Bad RequestThe request body is malformed, missing required fields, or contains invalid values.
401UnauthorizedAuthentication credentials are missing or invalid (expired token, revoked API key).
403ForbiddenThe authenticated user does not have the required scopes or permissions for this operation.
404Not FoundThe requested resource does not exist (e.g., unknown username, missing template).
406Not AcceptableThe Accept header specifies a media type the server cannot produce.
415Unsupported Media TypeThe Content-Type header is not supported. Most endpoints require application/json.
422Unprocessable EntityThe request was well-formed but could not be processed (e.g., missing required user attributes such as email or fund).
429Too Many RequestsRate limit exceeded. Check Retry-After header for when to retry.
500Internal Server ErrorAn unexpected error occurred on the server. Contact support if the issue persists.
502Bad GatewayAn upstream service returned an invalid response.

API Endpoints

Authenticated API endpoints for programmatic access to EDS analytics, portfolio management, charting, internal data, and user management.

Core

GET /datapull/v1/api/user_token/{username} eds-api-keyrate-limitedtoken.read

Get user token

Retrieves (or creates) an active API token for the given username. Requires the 'token.read' scope.

Path Parameters
NameTypeRequiredDescription
usernamestringYesThe target username to retrieve or create a token for.
Sample Request / Response
Request
GET https://harbour-api.equitydatascience.com/datapull/v1/api/user_token/jdoe
eds-api-key: <your-api-key>
Response
HTTP/1.1 200

{
  "username": "jdoe",
  "fund": "eds100",
  "token": "abc123-..."
}
Error Codes
400 401 403 404 422 429 500
GET /datapull/rate-limit-info eds-api-key

Get current rate limit status

Returns the current rate limit counters and remaining quota for the authenticated user.

Sample Request / Response
Request
GET https://harbour-api.equitydatascience.com/datapull/rate-limit-info
eds-api-key: <your-api-key>
Response
HTTP/1.1 200

{
  "rate_limiter": "slowapi",
  "info": {
    "auth_id": "jdoe",
    "auth_type": "username",
    "fund_id": "eds100",
    "redis_source": "graphite_redis",
    "limits": {
      "minute": {
        "used": 15,
        "limit": 5000
      },
      "hour": {
        "used": 100,
        "limit": 30000
      },
      "day": {
        "used": 200,
        "limit": 60000
      }
    }
  }
}
Error Codes
401 500

Portfolio Analytics

POST /datapull/v1/api/nexus_factors eds-api-keyrate-limited

Factor Exposures

Returns factor exposure data for a portfolio, including Z-scores, volatility-adjusted exposures, variance contributions, 30-day returns, and annualized volatility. Results are grouped by factor group (Market, Risk Indices, Industries, Countries, Currencies).

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
benchmarkstringNoBenchmark name or null for no benchmark.
proxyToStreetbooleanNoUse street-side proxy mappings. Default false.
currencyHedgebooleanNoApply currency hedge. Default false.
as_of_datestringNoReturn factor exposures as of this date (YYYY-MM-DD). Resolved to nearest prior trading day. Default null (latest available).
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_factors
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A",
  "factorModel": "GEMTRD",
  "aumType": "aum",
  "benchmark": null,
  "proxyToStreet": false,
  "currencyHedge": false
}
Response
HTTP/1.1 200

[
  {
    "Factor Group": "Market",
    "Factors": "World",
    "Z-Score": 2.0422,
    "Vol Adj Exp (Daily)": 0.014651,
    "% of Total Variance": 0.627096,
    "Ret (30D)": 0.028855,
    "Vol (Ann)": 0.113888
  },
  {
    "Factor Group": "Risk Indices",
    "Factors": "Beta",
    "Z-Score": 0.949855,
    "Vol Adj Exp (Daily)": 0.002932,
    "% of Total Variance": 0.107569,
    "Ret (30D)": -7.5e-05,
    "Vol (Ann)": 0.049004
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/nexus_summary eds-api-keyrate-limited

Portfolio Summary

Returns portfolio summary metrics including positions, AUM, exposures, beta, volatility, VaR, and IRR. Results are returned in Dollar and Percent sections. Optional valueType filter accepts: "$", "%", "Dollar", "Percent".

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
valueTypestringNoFilter results by value type. Options: "$", "%", "Dollar", "Percent".
as_of_datestringNoReturn portfolio summary as of this date (YYYY-MM-DD). Resolved to nearest prior trading day. Default null (latest available).
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_summary
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A",
  "factorModel": "GEMTRD",
  "aumType": "aum"
}
Response
HTTP/1.1 200

{
  "Dollar": {
    "Total Positions": 19,
    "Longs": 19,
    "Shorts": 0,
    "AUM": 617375528.86,
    "Gross": 1260804305.04,
    "Net": 1260804305.04,
    "Beta Net": 1565013113.15,
    "Vol Ann": 206328565.32,
    "VaR Ann": 339389857.1,
    "IRR": 0.492365
  },
  "Percent": {
    "Total Positions": 19,
    "Longs": 19,
    "Shorts": 0,
    "AUM": 1.0,
    "Gross": 2.0422,
    "Net": 2.0422,
    "Beta Net": 2.534945,
    "Vol Ann": 0.334203,
    "VaR Ann": 0.54973,
    "IRR": 0.492365
  }
}
Error Codes
400 401 403 429 500
POST /datapull/v1/api/columns eds-api-keyrate-limited

Available Grid Columns

Returns all available column definitions for the portfolio grid, grouped by source (e.g., Risk, Fundamentals, Holdings). Use the variable_name values as input for the nexus_portfolio_grid endpoint's columnNames parameter.

Body Parameters (application/json)
NameTypeRequiredDescription
rawPriceTargetsbooleanNoInclude raw price target columns. Default: true.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/columns
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "rawPriceTargets": true
}
Response
HTTP/1.1 200

{
  "Risk": [
    {
      "display_name": "% of Variance",
      "variable_name": "grid_pct_variance",
      "tooltip": "% of Variance",
      "data_type": "percent",
      "has_history": false
    },
    {
      "display_name": "Contrib to Portfolio Vol",
      "variable_name": "grid_vol_contri",
      "tooltip": "Contribution to Portfolio Vol",
      "data_type": "number",
      "has_history": false
    }
  ],
  "Holdings": [
    {
      "display_name": "Ticker",
      "variable_name": "ticker",
      "tooltip": "Ticker",
      "data_type": "string",
      "has_history": false
    }
  ]
}
Error Codes
400 401 403 429 500
POST /datapull/v1/api/nexus_portfolio_grid eds-api-keyrate-limited

Portfolio Grid Data

Returns portfolio grid data with selectable columns. Pass columnNames as an array of variable names (e.g., ["ticker", "priceusd", "country"]). Use the columns endpoint to discover available variable names. When portfolio is passed as an array of securities, null values are returned for Quantity, AUM, % of Gross, Ticker, % of AUM, Invested ($M), Strategy, and Gross.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
columnNamesarray[string]NoArray of column variable_name values from the columns endpoint.
viewIdstringNoSaved bookmark/view ID (UUID) to load column configuration from.
rawPriceTargetsbooleanNoInclude raw price target columns. Default: true.
as_of_datestringNoReturn portfolio positions as of this date (YYYY-MM-DD). Resolved to nearest prior trading day. Default null (latest available).
appliedPPKarray[string]NoPPK column names to net and aggregate positions by (e.g. ["strategy"]). When omitted, positions are aggregated by factset_id only.
ppk_filtersobjectNoFilter to a specific PPK sub-group and renormalise weights (e.g. {"strategy": ["Long Only"]}). When omitted, all sub-groups are included.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_portfolio_grid
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A",
  "factorModel": "GEMTRD",
  "aumType": "aum",
  "columnNames": [
    "ticker",
    "priceusd",
    "country"
  ]
}
Response
HTTP/1.1 200

[
  {
    "ticker": "CRH US",
    "grid_pct_factor": 0.009876
  },
  {
    "ticker": "AMZN US",
    "grid_pct_factor": 0.062986
  },
  {
    "ticker": "BAC US",
    "grid_pct_factor": 0.047578
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/nexus_optimizer eds-api-keyrate-limited

Portfolio Optimizer

Optimizes portfolio allocations using various objective functions and constraints. objectiveFunction values: "maximumIdio", "maximumExpReturn", "minimumTrading", "balancedVariance", "riskPriority". Constraints include portfolio-level (maxTurnover, maxVol, maxNetExposure, etc.), position-level (maxLongPosition, maxShortPosition), risk (minIdio, factorConstraints), beta (maxNetBeta, minNetBeta), and holdings (maxLongHoldings, maxShortHoldings).

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostring | arrayYesPortfolio name or array of [ticker, weight] pairs (e.g. [["CRH US", 0.16], ["AMZN US", 0.12]]).
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumnumberNoAssets under management in dollars.
objectiveFunctionstringYesOptimization objective. Options: "maximumIdio", "maximumExpReturn", "minimumTrading", "balancedVariance".
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
constraintsobjectNoOptimization constraints object. Keys include maxTurnover, maxVol, maxNetExposure, minNetExposure, minGrossExposure, maxGrossExposure, minIdio, maxNetBeta, minNetBeta, maxLongPosition, maxShortPosition, maxLongHoldings, maxShortHoldings, factorConstraints.
simulateByGroupbooleanNoSimulate optimization by factor group. Default false.
proxyToStreetbooleanNoUse street-side proxy mappings. Default false.
currencyHedgebooleanNoApply currency hedge. Default false.
excludePortfoliobooleanNoExclude current portfolio positions from optimization. Default false.
watchlistTickersarray[string]NoArray of tickers to restrict the optimization universe to.
as_of_datestringNoRun optimization as of a specific date (YYYY-MM-DD). Default null (latest available).
custom_position_constraintsobjectNoPer-position constraints keyed by ticker (e.g. min/max weight overrides).
featureFlagsobjectNoFeature flag overrides for experimental optimizer behaviour.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_optimizer
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": [
    [
      "CRH US",
      0.1646
    ],
    [
      "COF US",
      0.1418
    ],
    [
      "AMZN US",
      0.1157
    ],
    [
      "BAC US",
      0.1099
    ]
  ],
  "factorModel": "GEMTRD",
  "aum": 10000000,
  "aumType": "aum",
  "objectiveFunction": "minimumTrading",
  "constraints": {
    "maxVol": 16,
    "maxNetExposure": 8,
    "minNetExposure": -8,
    "minGrossExposure": 95,
    "maxGrossExposure": 105,
    "minIdio": 0.25
  },
  "simulateByGroup": false,
  "proxyToStreet": false,
  "currencyHedge": true
}
Response
HTTP/1.1 200

[
  {
    "ticker": "CRH US",
    "current_weight": 0.1646,
    "optimal_weight": 0.17,
    "trade": 0.0054
  },
  {
    "ticker": "COF US",
    "current_weight": 0.1418,
    "optimal_weight": 0.14,
    "trade": -0.0018
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/holdings eds-api-keyrate-limited

Portfolio Holdings

Returns the current holdings for a portfolio.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/holdings
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A"
}
Response
HTTP/1.1 200

[
  {
    "factset_id": "0P000003MH",
    "bbg_ticker": "AAPL US",
    "security_type": "Common Stock",
    "figi": "BBG000B9XRY4"
  },
  {
    "factset_id": "0HGQ8W-E",
    "bbg_ticker": "MSFT US",
    "security_type": "Common Stock",
    "figi": "BBG000BPH459"
  },
  {
    "factset_id": "MH33D6-R",
    "bbg_ticker": null,
    "security_type": null,
    "figi": null
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/portfolio_data eds-api-keyrate-limited

Portfolio Data

Returns portfolio position data including factset_id, ticker, description, pct_aum, instrument_type, and all portfolio primary key (PPK) columns for the specified portfolio. Paginated at 500 records per page. Response includes data array, total record count, page number, and total pages.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringYesPortfolio name (e.g. "Fund A").
pageintegerNoPage number (starts at 1). Default 1. Each page returns up to 500 records.
as_of_datestringNoReturn portfolio as of this date (YYYY-MM-DD). Falls back to the nearest prior available date if the exact date is not found (e.g. weekends). Default null (latest available snapshot).
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/portfolio_data
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A",
  "page": 1,
  "as_of_date": null
}
Response
HTTP/1.1 200

{
  "data": [
    {
      "factset_id": "0P000003MH",
      "ticker": "AAPL US",
      "description": "Apple Inc.",
      "pct_aum": 0.0523,
      "instrument_type": "Common Stock",
      "strategy": "Long Only"
    },
    {
      "factset_id": "0HGQ8W-E",
      "ticker": "MSFT US",
      "description": "Microsoft Corp.",
      "pct_aum": 0.0412,
      "instrument_type": "Common Stock",
      "strategy": "Long Only"
    }
  ],
  "total": 750,
  "page": 1,
  "total_pages": 2,
  "portfoliodate": "2025-12-04"
}
Error Codes
400 401 403 429 500
Note: Response includes all portfolio primary key (PPK) columns configured for the fund (e.g. strategy, fundname). These vary by fund. Results are paginated at 500 records per page. The portfoliodate field shows the actual date used for the data.
POST /datapull/v1/api/nexus_bookmarks eds-api-keyrate-limited

Saved Nexus Bookmarks

Returns the list of saved Nexus bookmarks (views) for the authenticated user. Includes both user-owned bookmarks and shared (grouped) bookmarks visible to the user's fund. Use the returned viewId values to load saved portfolio views. No request body parameters are required.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_bookmarks
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "viewId": 42,
    "viewName": "My Risk View",
    "sharedWithMe": false
  },
  {
    "viewId": 87,
    "viewName": "Team Factor View",
    "sharedWithMe": true
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/columns_data eds-api-keyrate-limited

Column Data Values

Returns calculated column values for securities in a portfolio. Resolves each data key through a hybrid lookup: if the key exists as a generic config column (e.g., FactSet fundamentals), the financial data source is used; otherwise it falls back to fund-scoped internal data. Use the columns or internal_data_key_list endpoints to discover available data keys.

Body Parameters (application/json)
NameTypeRequiredDescription
identifierstring | array[string]NoBloomberg ticker or FactSet ID. Pass a single string for one security, or an array for multiple. Required if portfolio is not provided.
portfoliostringNoPortfolio name — fetches data for all current holdings. Required if identifier is not provided.
data_keystring | array[string]YesColumn variable_name to retrieve. Pass a single string for one column, or an array for multiple. Multiple keys are fetched in parallel and merged by identifier.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/columns_data
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "identifier": [
    "AAPL US",
    "GOOGL US"
  ],
  "data_key": [
    "priceusd",
    "country",
    "sector"
  ]
}
Response
HTTP/1.1 200

[
  {
    "identifier": "AAPL US",
    "priceusd": 189.3,
    "country": "UNITED STATES",
    "sector": "Information Technology"
  },
  {
    "identifier": "GOOGL US",
    "priceusd": 415.2,
    "country": "UNITED STATES",
    "sector": "Communication Services"
  }
]
Error Codes
400 401 403 429 500
Note: Exactly one of 'identifier' or 'portfolio' must be provided. Passing both or neither returns a 400 error.
POST /datapull/v1/api/proxy_mappings eds-api-keyrate-limited

Factor Model Proxy Mappings

Returns factor model proxy mappings for the authenticated user's fund. Each mapping shows the original security, its proxy, and a description. No request body parameters are required.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/proxy_mappings
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "proxied_factsetid": "AAPL-USA",
    "factset_id": "MSFT-USA",
    "description": "Apple Inc."
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/nexus_intraday eds-api-keyrate-limited

Intraday Return & Factor Attribution

Returns intraday return and factor attribution data per security. Response columns include intraday_return_percent, intraday_return_amount, factor_return_contribution_percent, factor_return_contribution_amount, idio_return_contribution_percent, idio_return_contribution_amount, currency_return_contribution_percent, currency_return_contribution_amount, and their unweighted variants. Requires Redis intraday ticker/factor returns to be populated. When the factor model does not cover securities, factor contribution columns are returned as null. When as_of_date is provided, only ticker stubs (factsetId) are returned without intraday columns, since intraday data is only available for the current trading session.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostring | arrayNoPortfolio name or array of [ticker, weight] pairs. When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
proxyToStreetbooleanNoUse street-side proxy mappings. Default false.
currencyHedgebooleanNoApply currency hedge. Default false.
lookThroughbooleanNoEnable look-through for fund-of-fund holdings. Default false.
as_of_datestringNoHistorical date (YYYY-MM-DD). When provided, only ticker stubs (factsetId) are returned without intraday return columns, since intraday data is only available for the current trading session.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/nexus_intraday
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund A",
  "factorModel": "GEMTRD",
  "aumType": "aum",
  "currencyHedge": false
}
Response
HTTP/1.1 200

[
  {
    "factsetId": "0P000003MH",
    "index": 0,
    "intraday_return_percent": 0.000523,
    "intraday_return_amount": 0.322891,
    "factor_return_contribution_percent": 0.000312,
    "factor_return_contribution_amount": 0.192654,
    "idio_return_contribution_percent": 0.000211,
    "idio_return_contribution_amount": 0.130237,
    "currency_return_contribution_percent": 0.0,
    "currency_return_contribution_amount": 0.0,
    "unweighted_return_intraday": 0.00312,
    "unweighted_factor_return": 0.00186,
    "unweighted_idio_return": 0.00126,
    "unweighted_currency_return": 0.0,
    "pctequity": 0.1676
  }
]
Error Codes
400 401 403 429 500
Note: Intraday data is only available for the current trading session. Passing as_of_date returns ticker stubs without intraday columns.

MyPortfolio

POST /datapull/v1/api/myportfolio eds-api-keyrate-limited

MyPortfolio Widget Data

Returns data for a specified MyPortfolio widget. Only portfolio names are supported (not array format). Filters must match exactly as they appear on the widget in the MyPortfolio page.

Body Parameters (application/json)
NameTypeRequiredDescription
widgetNamestringYesName of the MyPortfolio widget to retrieve data for.
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
benchmarkstringNoBenchmark name or null for no benchmark.
adrProxiesbooleanNoUse ADR proxy mappings. Default false.
currencyHedgebooleanNoApply currency hedge. Default false.
lookthroughbooleanNoEnable look-through for fund-of-fund holdings. Default false.
filtersobjectNoWidget filter overrides as key-value pairs (e.g. {"direction": ["long"]}). Available filters depend on the widget. When omitted, widget defaults are used.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/myportfolio
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "widgetName": "Category Risk Contribution",
  "portfolio": "Fund B",
  "factorModel": "GEMTRD",
  "aumType": "aum",
  "benchmark": null,
  "adrProxies": false,
  "currencyHedge": true,
  "lookthrough": false
}
Response
HTTP/1.1 200

[
  {
    "Sector (GICS)": "Health Care",
    "Net Portfolio Exposure": "18.14%",
    "Net Beta Exposure": "14.19%",
    "Portfolio Vol": "4.33%",
    "Idio Vol": "2.0%",
    "% of Variance": "36.71%",
    "% of Idio": "19.71%",
    "Idio % of Variance": "7.8%"
  },
  {
    "Sector (GICS)": "Industrials",
    "Net Portfolio Exposure": "7.58%",
    "Net Beta Exposure": "10.23%",
    "Portfolio Vol": "3.21%",
    "Idio Vol": "2.25%",
    "% of Variance": "20.23%",
    "% of Idio": "24.96%",
    "Idio % of Variance": "9.87%"
  }
]
Error Codes
400 401 403 429 500
Note: Example widget filters for Category Risk Contribution: Direction (["long"], ["short"], ["long", "short"]), Attribution Category ("Sector (GICS)", "Industry (GICS)", "Country"), Column Sorting Options, Column Sorting Order, Row Options, Grouping Level. Example filters for Factor and Idio Returns: Sector GICS (["Materials", "Financials"]), Country GICS (["United States", "Japan"]), Direction (["long"]), Metrics (["Idio", "Factor", "Total"]), Manager (["Manager - Fund B"]).

Charting

POST /datapull/v1/api/charting_metrics eds-api-keyrate-limited

Available Charting Metrics

Returns available charting metric groups and their individual metrics. Use the metric_id values in the charting endpoint's selected_metrics parameter.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/charting_metrics
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "metric_group_id": "8a9d15e4-...",
    "metric_group_name": "Z-Score",
    "metrics": [
      {
        "metric_id": "002d1f73-...",
        "display_name": "Market Z-Score"
      }
    ]
  },
  {
    "metric_group_id": "d5f529c9-...",
    "metric_group_name": "Exposures %",
    "metrics": [
      {
        "metric_id": "05b79b4c-...",
        "display_name": "Net Exposure %"
      }
    ]
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/charting_assets eds-api-keyrate-limited

Available Charting Assets

Returns available charting asset groups (Factors, Securities, Portfolios) and their individual assets. Use the asset_id values in the charting endpoint's selected_asset parameter.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/charting_assets
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "asset_group_id": "48241bca-...",
    "asset_group_name": "Factors",
    "assets": [
      {
        "asset_id": "000e91f7-...",
        "display_name": "Market"
      }
    ]
  },
  {
    "asset_group_id": "be82d163-...",
    "asset_group_name": "Securities",
    "assets": [
      {
        "asset_id": "0A8QVM-D",
        "display_name": "S&P 500"
      }
    ]
  },
  {
    "asset_group_id": "c86fb42d-...",
    "asset_group_name": "Portfolios",
    "assets": [
      {
        "asset_id": "eds100_1",
        "display_name": "Fund C"
      }
    ]
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/charting eds-api-keyrate-limited

Chart Data

Returns time-series chart data for selected metrics and assets. Use charting_metrics and charting_assets endpoints to discover available metric_id and asset_id values.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name (e.g. "Fund A"). When omitted, the user's default portfolio is used.
factorModelstringNoFactor model identifier (e.g. "GEMTRD", "GEMLTD", "AXUS4S"). When omitted, the user's default factor model is used.
selected_assetstringYesAsset ID from the charting_assets endpoint.
selected_metricsarray[object]YesArray of metric objects, each with a metric_id from the charting_metrics endpoint.
daterangeobjectNoDate range with start_date and end_date in YYYY-MM-DD format.
globalSettingsobjectNoGlobal settings object with lookThrough, proxyToStreet, currencyHedge, relatedSecuritiesProxies booleans.
aumTypestringNoAUM type. Default "aum". Options: "aum", "custom".
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/charting
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund B",
  "factorModel": "GEMTRD",
  "selected_asset": "eds100_fund1",
  "selected_metrics": [
    {
      "metric_id": "17cbff15-7c37-4e61-8069-649fe94bb936"
    },
    {
      "metric_id": "d5e57964-0388-4532-9a55-33a8c50d4ea3"
    }
  ],
  "daterange": {
    "start_date": "2024-10-01",
    "end_date": "2025-09-11"
  },
  "globalSettings": {
    "lookThrough": false,
    "proxyToStreet": false,
    "currencyHedge": true,
    "relatedSecuritiesProxies": false
  },
  "aumType": "aum"
}
Response
HTTP/1.1 200

{
  "data": [
    {
      "date": "2024-10-01",
      "17cbff15-...": 0.0512,
      "d5e57964-...": 1.245
    },
    {
      "date": "2024-10-02",
      "17cbff15-...": 0.0498,
      "d5e57964-...": 1.251
    }
  ],
  "selected_metrics": [
    {
      "metric_id": "17cbff15-...",
      "display_name": "Net Exposure %"
    },
    {
      "metric_id": "d5e57964-...",
      "display_name": "Gross Exposure %"
    }
  ]
}
Error Codes
400 401 403 429 500

Internal Data

POST /datapull/v1/api/internal_data_key_list eds-api-keyrate-limited

Available Internal Data Keys

Returns all available internal data field IDs for your fund. Use these data_key values in the internal_data_history endpoint. No request body parameters are required.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/internal_data_key_list
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "source": "Excel Push",
    "data key": "30D CONSENSUS EPS REVISION"
  },
  {
    "source": "Excel Push",
    "data key": "ABSOLUTE FCF YIELD"
  },
  {
    "source": "Excel Push",
    "data key": "BLOOMBERG_TICKER"
  }
]
Error Codes
400 401 403 429 500
POST /datapull/v1/api/internal_data_history eds-api-keyrate-limited

Internal Data History

Returns historical internal data for securities or portfolios. Use either identifiers (array of tickers) or portfolio (portfolio name), but not both. Pass data_keys as an array of field IDs to retrieve. Optional start_date and end_date in YYYY-MM-DD format.

Body Parameters (application/json)
NameTypeRequiredDescription
portfoliostringNoPortfolio name. Required if identifiers is not provided.
identifiersarray[string]NoArray of security tickers. Required if portfolio is not provided.
data_keysarray[string]YesArray of internal data field IDs to retrieve.
start_datestringNoStart date in YYYY-MM-DD format.
end_datestringNoEnd date in YYYY-MM-DD format.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/internal_data_history
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "portfolio": "Fund B",
  "data_keys": [
    "EBIT_FY2026Q1"
  ],
  "start_date": "2024-09-10",
  "end_date": "2025-11-29"
}
Response
HTTP/1.1 200

{
  "2330 TT": [
    {
      "date": "2025-11-07",
      "EBIT_FY2026Q1": 524626.73
    },
    {
      "date": "2025-11-14",
      "EBIT_FY2026Q1": 521948.19
    }
  ],
  "AMZN US": [
    {
      "date": "2025-11-07",
      "EBIT_FY2026Q1": 32150.25
    }
  ]
}
Error Codes
400 401 403 429 500
Note: Exactly one of 'portfolio' or 'identifiers' must be provided. Passing both or neither returns a 400 error.
POST /datapull/v1/api/price_targets eds-api-keyrate-limited

price_targets

Body Parameters (application/json)
NameTypeRequiredDescription
identifierstringYesBloomberg ticker or FactSet ID.
portfoliostringNoPortfolio name to scope scenarios to. When omitted, scenarios are returned grouped by all accessible portfolios.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/price_targets
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "identifier": "AAPL US",
  "portfolio": "Fund A"
}
Response
HTTP/1.1 200

[
  {
    "price": 220.0,
    "probability": 0.6,
    "title": "Bull Case",
    "comment": "Strong earnings growth expected.",
    "targetDate": "2025-12-31",
    "identifier": "AAPL US",
    "figi": "BBG000B9XRY4",
    "analyst": "Jane Smith"
  },
  {
    "price": 160.0,
    "probability": 0.25,
    "title": "Bear Case",
    "comment": "Margin compression risk.",
    "targetDate": "2025-12-31",
    "identifier": "AAPL US",
    "figi": "BBG000B9XRY4",
    "analyst": "Jane Smith"
  }
]
Error Codes
400 401 403 429 500
Note: When 'portfolio' is omitted, the response is an array of objects grouped by portfolio ({"portfolio": "...", "scenarios": [...]}). When 'portfolio' is provided, the response is a flat array of scenario objects.

Research

POST /datapull/v1/api/research_grid eds-api-keyrate-limited

EDS Construction Dashboard

Returns data from the EDS Construction Dashboard for a specified view. Pass view as the view name (e.g., "summary dashboard"). Optionally filter by watchlist or portfolio names.

Body Parameters (application/json)
NameTypeRequiredDescription
viewstringYesView name (e.g. "summary dashboard"). Use the views endpoint to discover available views.
watchliststringNoWatchlist name to filter results.
portfoliostringNoPortfolio name to filter results.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/research_grid
eds-api-key: <your-api-key>
Content-Type: application/json

{
  "view": "summary dashboard",
  "watchlist": "Sample Watchlist"
}
Response
HTTP/1.1 200

[
  {
    "Ticker": "AMZN US",
    "Strategy": "Long/Short",
    "Instrument Type": "CommonStock",
    "Direction": "Long",
    "Name": "Amazon.com, Inc.",
    "Sector": "Consumer Discretionary",
    "Current Value USD": 5.112698
  }
]
Error Codes
400 401 403 429 500

Views

POST /datapull/v1/api/views eds-api-keyrate-limited

Saved Views

Returns all saved views accessible to the authenticated user. Each view includes its name, type, associated watchlists/portfolios, and column configuration. No request body parameters are required.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/views
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "view_name": "summary dashboard",
    "view_type": "watchlist",
    "watchlist": "Sample Watchlist"
  }
]
Error Codes
400 401 403 429 500

Admin

POST /datapull/v1/api/ai_users eds-api-keyrate-limitedtoken.read

AI-Enabled Users

Returns internal users with AI access. Lists .eds email users associated with funds that have linqalpha_organization_id set and are not internal funds. Requires the 'token.read' scope. No request body parameters are required.

Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/datapull/v1/api/ai_users
eds-api-key: <your-api-key>
Content-Type: application/json
Response
HTTP/1.1 200

[
  {
    "fundname": "Example Fund",
    "fund": "eds100",
    "username": "jdoe"
  }
]
Error Codes
400 401 403 429 500
Note: Requires the 'token.read' scope. Returns 403 if the scope is missing.

OAuth & Discovery

OAuth 2.0 authorization and OIDC/OAuth discovery endpoints. Supports authorization code flow with PKCE.

GET /oauth/authorize none

OAuth authorization redirect

Initiates the OAuth authorization flow. All query parameters (client_id, redirect_uri, scope, etc.) are forwarded to the authorization server.

Query Parameters
NameTypeRequiredDescription
client_idstringYesOAuth client ID.
redirect_uristringYesCallback URL after authorization.
response_typestringYesOAuth response type (e.g., 'code').
scopestringNoRequested scopes (e.g., 'openid profile email').
statestringNoCSRF state parameter.
code_challengestringNoPKCE code challenge.
code_challenge_methodstringNoPKCE method (S256).
Sample Request / Response
Request
GET https://harbour-api.equitydatascience.com/oauth/authorize?client_id=abc&redirect_uri=https://app.example.com/callback&response_type=code&scope=openid
Response
HTTP/1.1 302
Error Codes
502
Note: Returns a 302 redirect to the authorization server.
POST /oauth/token none

OAuth token exchange

Exchanges authorization codes, refresh tokens, or client credentials for access/refresh tokens.

Request Body (application/x-www-form-urlencoded)
grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://app.example.com/callback&client_id=abc
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://app.example.com/callback&client_id=abc
Response
HTTP/1.1 200

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "v1.abc...",
  "scope": "openid profile email"
}
Error Codes
400 500 502

MCP Protocol

Model Context Protocol (MCP) endpoints implementing Streamable HTTP transport (MCP specification 2025-11-25). Requires JWT bearer authentication.

POST /mcp jwt-bearerrate-limited

Send MCP JSON-RPC request

Accepts a JSON-RPC 2.0 request (or batch) for MCP operations such as tools/list, tools/call, resources/list, etc. Returns JSON or SSE stream.

Request Headers
NameRequiredDescription
Mcp-Session-IdNoSession ID for an existing MCP session. Omit for initialization.
Sample Request / Response
Request
POST https://harbour-api.equitydatascience.com/mcp
Authorization: Bearer <jwt-token>
Content-Type: application/json
Mcp-Session-Id: <session-id>

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}
Response
HTTP/1.1 200

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "example_tool",
        "description": "An example MCP tool",
        "inputSchema": {
          "type": "object"
        }
      }
    ]
  }
}
Error Codes
400 401 429 500
GET /mcp jwt-bearerrate-limited

Open MCP SSE stream

Opens a Server-Sent Events (SSE) stream for receiving MCP notifications and server-initiated messages within an active session.

Request Headers
NameRequiredDescription
Mcp-Session-IdYesSession ID for an existing MCP session.
Sample Request / Response
Request
GET https://harbour-api.equitydatascience.com/mcp
Authorization: Bearer <jwt-token>
Mcp-Session-Id: <session-id>
Accept: text/event-stream
Response
HTTP/1.1 200

event: message
data: {"jsonrpc": "2.0", ...}

Error Codes
401 429 500
Note: Response is a text/event-stream. Each event contains a JSON-RPC message.
DELETE /mcp jwt-bearerrate-limited

Terminate MCP session

Terminates an active MCP session and cleans up server-side resources.

Request Headers
NameRequiredDescription
Mcp-Session-IdYesSession ID of the MCP session to terminate.
Sample Request / Response
Request
DELETE https://harbour-api.equitydatascience.com/mcp
Authorization: Bearer <jwt-token>
Mcp-Session-Id: <session-id>
Response
HTTP/1.1 200
Error Codes
401 429 500
Note: Returns 200 with empty body on successful session termination.