klaviyo / klaviyo-api-python Goto Github PK
View Code? Open in Web Editor NEWPython SDK for Klaviyo API
Home Page: https://developers.klaviyo.com
License: MIT License
Python SDK for Klaviyo API
Home Page: https://developers.klaviyo.com
License: MIT License
Hi there.
i think that there is a but that causes regression with the unset profile tag functionality. it used to work for a few weeks and then this change caused it to break:
2ba4b48#r124652203
when trying to use the update_profile method, i get this error:
pydantic.error_wrappers.ValidationError: 1 validation error for ProfilePartialUpdateQuery
data -> meta -> patch_properties -> unset
value is not a valid dict (type=type_error.dict)
if i'm putting a dictionary, then i get an API error from your servers...
HTTP response body: {"errors":[{"id":"53bad77c-ed7a-497c-8ee1-33161572b398","status":400,"code":"invalid",
"title":"Invalid input.","detail":"Unset must be a string or an array of strings (the property keys to unset).",
"source":{"pointer":"/data/attributes/meta/patch_properties/unset"},"meta":{}}]}
i'm doing something like this:
profile_id = 'dummy-profile-id'
profile_update_data = {
'type': 'profile',
'id': profile_id,
'attributes': {},
'meta': {
'patch_properties': {
'unset': 'check_integration',
},
},
}
self.api.Profiles.update_profile(
profile_id,
ProfilePartialUpdateQuery(data=profile_update_data),
)
will appreciate your help
Hello there,
we started getting 400 errors today from klaviyo API get_events when filtering on specific metric ids.
this is the error:
HTTP response body: {"errors":[{"id":"1e7d5dd1-1af1-4599-9ae4-6e5f64bd66d9","status":400,"code":"invalid","title":"Invalid
input.","detail":"Metric IDs ['SOME_METRIC_ID'] do not exist.","source":{"parameter":"filter"},"meta":{}}]}
the thing is that we get SOME_METRIC_ID
from a previous call to get_metrics()
call from the api, so this is a legitimate metric as far as i undersatnd.
we saw this with multiple stores we operate, and wonder why is that happening. it is mostly happening for some metric ids that represent metrics like Viewed Form
, Closed Form
, subscribe_page_view
and some more...
wondering what we are missing, as other metric ids that get_metrics works just fine.
Looks like pages size is not implemented per docs:
klaviyo.Profiles.get_profiles(fields_profile=fields_profile, filter=filter, page_cursor=page_cursor, sort=sort, page_size=page_size)
Hey all,
relatively recently (yesterday?) I started receiving error 500 when trying to get a single template with the Templates.get_template endpoint. It seems like the .get_templates is still working so I can access the templates that way. I am using 3.0.0 of the klaviyo api
If it helps here is the exception:
ServiceException: Status Code: 500 Reason: Internal Server Error HTTP response headers: HTTPHeaderDict({'Date': 'Tue, 27 Jun 2023 14:01:23 GMT', 'Content-Type': 'application/vnd.api+json', 'Content-Length': '234', 'Connection': 'keep-alive', 'CF-Ray': '7dde38bf69c2b734-AMS', 'CF-Cache-Status': 'DYNAMIC', 'Allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Vary': 'Cookie, Accept-Encoding', 'CID': 'UeZz4r', 'Content-Security-Policy-Report-Only': "base-uri 'none'; script-src 'strict-dynamic' 'unsafe-eval'; frame-ancestors 'self'; object-src 'none'", 'RateLimit-Limit': '150, 10;w=1, 150;w=60', 'RateLimit-Remaining': '149', 'RateLimit-Reset': '22', 'X-Content-Type-Options': 'nosniff', 'X-Klaviyo-API-Revision': '2022-10-17', 'X-Robots-Tag': 'noindex, nofollow', 'Server': 'cloudflare'}) HTTP response body: {"errors":[{"id":"a73df42f-251d-4bf3-99bb-841fa26c2446","status":500,"code":"error","title":"A server error occurred.","detail":"While structuring TemplateResponseDeprecated (1 sub-exception)","source":{"pointer":"/data"},"meta":{}}]}
Best,
David
Hi ๐,
I noticed that the fields[profile]
does not seem to work correctly for nested attributes at least for Get list profiles
.
Example:
If I search for a non existing profile attribute I get the following response:
Invalid values for `fields_profile` [('blah',)], must be a subset of [email, phone_number, external_id, first_name, last_name, organization, title, image, created, updated, last_event_date, location, location.address1, location.address2, location.city, location.country, location.latitude, location.longitude, location.region, location.zip, location.timezone, properties, subscriptions, subscriptions.email, subscriptions.email.marketing, subscriptions.email.marketing.consent, subscriptions.email.marketing.timestamp, subscriptions.email.marketing.method, subscriptions.email.marketing.method_detail, subscriptions.email.marketing.custom_method_detail, subscriptions.email.marketing.double_optin, subscriptions.email.marketing.suppressions, subscriptions.email.marketing.suppressions.reason, subscriptions.email.marketing.suppressions.timestamp, subscriptions.email.marketing.list_suppressions, subscriptions.email.marketing.list_suppressions.list_id, subscriptions.email.marketing.list_suppressions.reason, subscriptions.email.marketing.list_suppressions.timestamp, subscriptions.sms, subscriptions.sms.marketing, subscriptions.sms.marketing.consent, subscriptions.sms.marketing.timestamp, subscriptions.sms.marketing.method, subscriptions.sms.marketing.method_detail, predictive_analytics, predictive_analytics.historic_clv, predictive_analytics.predicted_clv, predictive_analytics.total_clv, predictive_analytics.historic_number_of_orders, predictive_analytics.predicted_number_of_orders, predictive_analytics.average_days_between_orders, predictive_analytics.average_order_value, predictive_analytics.churn_probability, predictive_analytics.expected_date_of_next_order]
However, if I use one of these nested attributes like subscriptions.email.marketing.consent
, I get:
HTTP response body: {"errors":[{"id":"487e7cda-7e6b-47a4-822d-0e989ac46d29","status":400,"code":"invalid","title":"Invalid input.","detail":"'subscriptions.email.marketing.consent' is not a nested object on the 'profile' resource","source":{"parameter":"fields"},"meta":{}}]}
For non-nested attributes it works normally so far. I have not tested it on other endpoints that support fields[profile]
.
Best,
Hagen
When making calls to update_profile using the following:
from klaviyo_api import KlaviyoAPI
client = KlaviyoAPI({API key here})
id = {An existing Profile id}
profile = {Some dict, content is irrelevant}
client.Profiles.update_profile(id=id, profile_partial_update_query=profile)
I get the following stack trace
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\simen\cutters-marketing\env\lib\site-packages\klaviyo_api\wrapper.py", line 332, in _wrapped_func return func(*args,**kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\tenacity\__init__.py", line 289, in wrapped_f return self(f, *args, **kw) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\tenacity\__init__.py", line 379, in __call__ do = self.iter(retry_state=retry_state) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\tenacity\__init__.py", line 314, in iter return fut.result() File "C:\Users\simen\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\_base.py", line 428, in result return self.__get_result() File "C:\Users\simen\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\_base.py", line 384, in __get_result raise self._exception File "C:\Users\simen\cutters-marketing\env\lib\site-packages\tenacity\__init__.py", line 382, in __call__ result = fn(*args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\api\profiles_api.py", line 1939, in update_profile return self.update_profile_endpoint.call_with_http_info(**kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\api_client.py", line 872, in call_with_http_info self.__validate_inputs(kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\api_client.py", line 759, in __validate_inputs configuration=self.api_client.configuration File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1591, in validate_and_convert_types check_type=_check_type File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1465, in attempt_convert_item configuration, spec_property_naming) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1377, in deserialize_model return model_class._new_from_openapi_data(**kw_args) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 49, in wrapped_init return fn(_self, *args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 372, in _new_from_openapi_data return cls._from_openapi_data(*args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 49, in wrapped_init return fn(_self, *args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model\profile_partial_update_query.py", line 178, in _from_openapi_data self.data = data File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 190, in __setattr__ self[attr] = value File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 511, in __setitem__ self.set_attribute(name, value) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 164, in set_attribute self._check_type, configuration=self._configuration) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1591, in validate_and_convert_types check_type=_check_type File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1465, in attempt_convert_item configuration, spec_property_naming) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 1377, in deserialize_model return model_class._new_from_openapi_data(**kw_args) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 49, in wrapped_init return fn(_self, *args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 372, in _new_from_openapi_data return cls._from_openapi_data(*args, **kwargs) File "C:\Users\simen\cutters-marketing\env\lib\site-packages\openapi_client\model_utils.py", line 49, in wrapped_init return fn(_self, *args, **kwargs) TypeError: _from_openapi_data() missing 1 required positional argument: 'id'
This is a very confusing message to receive, as I have explicitly provided that argument.
Making the function call without keywords, i.e. client.Profiles.update_profile(id, profile)
results in the same behaviour
The same KlaviyoAPI instance can be used to successfully make other queries. There appears to be something wrong with the parameter validation of Profiles.update_profile
I have been using vcrpy to record API calls and replay them in tests. Since upgrading this library to v8.0.0, get_profiles requests are failing due to a cassette mismatch.
Using get_profiles to filter based on an email now duplicates the filter parameter causing the mismatch.
Query: klaviyo.Profiles.get_profiles(filter=f'equals(email,"{email}")')
V7.0.0 would construct a query as: https://a.klaviyo.com/api/profiles/?filter=equals%28email%2C%22test%2Bnewsubscriber%40gmail.com%22%29
V8.0.0 now creates: https://a.klaviyo.com/api/profiles/?filter=equals%28email%2C%22test%2Bnewsubscriber%40gmail.com%22%29&filter=equals%28email%2C%22test%2Bnewsubscriber%40gmail.com%22%29
I'm not noticing an issue with the results which are returned in V8.0.0, however I do need to regenerate test yml files or ignore the filter query param to get tests passing again.
Based on the API docs, flows should be paginated with offset pagination : https://developers.klaviyo.com/en/reference/get_flows
When fetching the first page we get a link to a new page. You cannot specify the offset/page cursor in the keyword arguments, like with other Klaviyo resources/objects
There is no way to paginate Get Flows with this library based on what I tried
When requesting profiles with KlaviyoAPI().Profiles.get_profiles()
, I'm receiving some that are not in E.164 format. Some come back like "(###) ###-####". Accessing a profile in the console shows that the contact information for such profiles do indeed have them saved in non-standard formats. Shouldn't these values be coerced into E.164 format prior to being sent back in an API response?
body= {
"data":{
"type":"event",
"attributes":{
"properties":{"key": "value"},
"metric": {
"data": {
"type": "metric",
"attributes": {
"name": "verification_completed"
}
}
},
"profile":{
"data":{
"type":"profile",
"id":"user_id",
"attributes": {
"email": "[email protected]"
}
}
}
}
}
}
try:
klaviyo.Events.create_event(body)
except Exception as err:
print(err)
With the old SDK the Get all templates call was with page count pagination :
https://github.com/klaviyo/klaviyo-python-sdk#templates
https://developers.klaviyo.com/en/v1-2/reference/get-templates
With the new SDK (and api) Get all templates is paginated with the cursor : https://developers.klaviyo.com/en/reference/get_templates
https://github.com/klaviyo/klaviyo-api-python#get-templates
I have 29 templates in my Klaviyo account. fetching with the old client/api i get 29.
Fetching with the new client/api I get only the 20 from the first page and the next cursor is none:
'links' : {'self': 'https://a.klaviyo.com/api/templates/', 'next': None, 'prev': None}
is this an issue with the API or SDK or is there something im doing wrong?
I just run
client = KlaviyoAPI(api_token, max_delay=60, max_retries=5)
first_page = client.Templates.get_templates()
and the first page has the above links with no next page
In v7.0.0, I was able to pass in async_req=False to subscribe_profiles
to synchronously subscribe a profile.
Currently, to subscribe a profile I need to make the following API calls:
get_profiles
by email to determine whether the profile is already subscribed to our main listsubscribe_profiles
to subscribe the profile, including below if they aren't already subscribed to the main list:data["data"]["relationships"] = {
"list": {
"data": {
"type": "list",
"id": list_id
}
}
}
get_profiles
call again to ensure I have a profile IDupdate_profile
call to set the users first name, last name, and custom propertiesWith V8.0.0 I would also need to take into account that subscribe_profiles
is async and would need to move 3 + 4 into a background job which executes once the profile is available.
All of this seems like a lot of overhead to subscribe a user and set their first name, last name, and a city. For now I'm going to keep our app at V7.0.0 but please let me know if there's a better approach which would be possible.
Calling the following method with the filter:
klaviyo.Profiles.get_profiles(filter="greater-than(created,2022-07-26T13:36:45+00:00),less-than(created,2022-07-26T13:36:47+00:00)"
I am expecting profiles that were created between entered dates.
But it seems that I am getting all the profiles.
data= {
"data":{
"type":"event",
"attributes":{
"properties":{"key": "value"},
"metric": {
"data": {
"type": "metric",
"attributes": {
"name": "verification_completed"
}
}
},
"profile":{
"data":{
"type":"profile",
"id":"user_id",
"attributes": {
"email": "[email protected]"
}
}
}
}
}
}
try:
klaviyo.Events.create_event(event_create_query=data)
except Exception as err:
print(err)
Hi klaviyo devs ๐
My app server relies on some features of pydantic only available in v > 2.0, which yields incompatibility issues with the klaviyo client. Is pydantic v2 compatibility anywhere on the roadmap ?
Thanks !
We'd like to use templates from Klaviyo to send some emails ourselves. We can get the templates via the API and, of course, it'd be inefficient to call the API for every single email we want to send. We would rather avoid implementing the parsing ourselves. However, it doesn't seem as if this provides any rendering ability even if wholly manual (because none of the profile properties and/or other macros available in the UI would be present/available in this context). Thoughts/suggestions? Is this a standard templating engine/dialect that I can use from Python to render?
Hi!
Sometimes I get the following error message when calling get_events() with a cursor URL:
Status Code: 400
Reason: Bad Request
...
"status":400,"code":"invalid","title":"Invalid input.","detail":"Invalid cursor provided.","source":{"pointer":"/data/"}}]}
When I input this cursor URL to Postman, I get a normal response.
This error seems to occur more with older data.
Thanks in advance!
On other SDK, I can tell if I am approaching a limit on the number of requests, and be able to "sleep" before resuming my operations. Is there a way to prevent the API request from failing. Basically, I am asking if I can query my current limit, take proactive measures to slow down my requests before an error occurs.
HTTP response headers: HTTPHeaderDict({'Date': 'Thu, 23 Mar 2023 01:02:14 GMT', 'Content-Type': 'application[/vnd.api](https://file+.vscode-resource.vscode-cdn.net/vnd.api)+json', 'Content-Length': '220', 'Connection': 'keep-alive', 'CF-Ray': 'xxxxxxxxxxxxx-EWR', 'Allow': 'GET, POST, HEAD, OPTIONS', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Vary': 'Cookie, Accept-Encoding', 'CF-Cache-Status': 'DYNAMIC', 'CID': 'xxxxx', 'Content-Security-Policy-Report-Only': "object-src 'none'; frame-ancestors 'self'; script-src 'strict-dynamic' 'unsafe-eval'; base-uri 'none'", 'X-Content-Type-Options': 'nosniff', 'X-Robots-Tag': 'noindex, nofollow', 'Server': 'cloudflare'}) HTTP response body: {"errors":[{"id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","status":429,"code":"throttled","title":"Request was throttled.","detail":"Request was throttled. Expected available in 0 seconds.","source":{"pointer":"[/data/](https://file+.vscode-resource.vscode-cdn.net/data/)"}}]}
Hi there ๐
First of all: Thanks for the cool library ๐ช Makes it really convenient to work with your API. I am a big fan!
However, I am missing two very important features right now that were somewhat besser supported with the legacy API and which I think are crucial and I would kindly make a feature request.
1. Bulk setting of user properties
Even though it was not directly supported, it was possible to bulk-set properties of users via
import requests
import json
API_KEY = 'APIKEY'
LIST_ID = 'LISTID'
headers = {
'Content-Type': 'application/json',
'api-key': API_KEY,
}
data = {
'profiles': [
{
'email': '[email protected]',
'properties': {
'test_property': 'valueA',
},
'confirm_optin': False,
},
{
'email': '[email protected]',
'properties': {
'test_property': 'valueB',
},
'confirm_optin': False,
},
],
}
response = requests.post(f'https://a.klaviyo.com/api/v2/list/{LIST_ID}/members', headers=headers, data=json.dumps(data))
That was super convenient and fast. Now, If you want to set properties you can only do it per user and even though the rate limits are generous, it takes A LOT of time for a significant amount of users to assign custom properties.
2. Unsetting of user properties
In the legacy API it was possible to unset properties like
import requests
import json
api_key = ...
profile_id = ...
attribute_name = ...
url = f"https://a.klaviyo.com/api/v1/person/{profile_id}?api_key={api_key}"
data = {
"$unset": [attribute_name],
}
response = requests.put(url, data=json.dumps(data))
print(response.text)
This does not seem to be possible anymore too. After sunsetting the legacy API there is no way to clean-up user profiles.
It would be great if those features would find their way again into the API, as I think they are crucial for working with user profiles.
Thank you for your attention,
Hagen
Klaviyo throws exceptions sometime on local machine:
Klaviyo profile unable to subscribe: due to HTTPSConnectionPool(host='a.klaviyo.com', port=443): Max retries exceeded with url: /api/profile-subscription-bulk-create-jobs/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)')))
Hi there,
we use the API to read the profiles of a specific campaign. in order to achieve that we take the audience object and for each includes
id we fetch the profiles via the list api. i have 2 questions:
Lists.get_list(id)
- we have a permission problem (we get 403 response)as it requires list:write (although Lists.get_lists()
) work with the same token) - is this a bug or are we missing something?Hi!
I am trying to get Segment Profiles with:
klaviyo.Segments.get_segment_profiles(segment_id, fields_profile=fields_profile, filter=filter, page_cursor=page_cursor)
Is it possible to specify how many results I want to be returned with the page[size] parameter?
Thanks!
Forgive me for my ignorance, but after perusing the documentation it's not clear how we would migrate the following code to this new api:
# old api:
import klaviyo
klaviyo_client = klaviyo.Klaviyo(
public_token=TOKEN)
then
klaviyo_client.Public.track(
event_name,
email=email,
properties=event_properties,
customer_properties=customer_properties)
or
klaviyo_client.Public.identify(
email=email,
properties=customer_properties
)
Any help? can this be added to the documentation? This all we use the python Klaviyo API for right now -- tracking events from our app that trigger flows and updating user profiles with content from our app.
Hi there,
I noticed that, starting about 1-2 weeks ago, about one every 10-20 requests (roughly 5-10% of calls) made using the Klaviyo API for Python hang and cause my containing Django app to timeout.
I'm using klaviyo-api==2.0.0
inside a Django 3.2.11 app, which runs on a python:3.8-slim-buster
with no further modifications made.
This is an issue exclusive to calls made to Klaviyo and I've excluded any connectivity issues on my side.
Any ideas, fixes or workarounds would be highly appreciated. Let me know if there is any other information I can provide.
Below is a stack trace for calls made to Profiles.create_profile()
, the program is stuck on return self._sslobj.read(len, buffer)
until the gunicorn
timeout is hit:
profile_id = klaviyo_client.Profiles.create_profile(body)
File "klaviyo_api/wrapper.py", line 330, in _wrapped_func
return func(*args,**kwargs)
File "__init__.py", line 324, in wrapped_f
return self(f, *args, **kw)
File "__init__.py", line 404, in __call__
do = self.iter(retry_state=retry_state)
File "__init__.py", line 349, in iter
return fut.result()
File "concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "concurrent/futures/_base.py", line 388, in __get_result
raise self._exception
File "__init__.py", line 407, in __call__
result = fn(*args, **kwargs)
File "openapi_client/api/profiles_api.py", line 1013, in create_profile
return self.create_profile_endpoint.call_with_http_info(**kwargs)
File "openapi_client/api_client.py", line 893, in call_with_http_info
return self.api_client.call_api(
File "openapi_client/api_client.py", line 422, in call_api
return self.__call_api(resource_path, method,
File "openapi_client/api_client.py", line 199, in __call_api
response_data = self.request(
File "openapi_client/api_client.py", line 468, in request
return self.rest_client.POST(url,
File "openapi_client/rest.py", line 271, in POST
return self.request("POST", url,
File "openapi_client/rest.py", line 157, in request
r = self.pool_manager.request(
File "urllib3/request.py", line 79, in request
return self.request_encode_body(
File "urllib3/request.py", line 171, in request_encode_body
return self.urlopen(method, url, **extra_kw)
File "urllib3/poolmanager.py", line 336, in urlopen
response = conn.urlopen(method, u.request_uri, **kw)
File "urllib3/connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "urllib3/connectionpool.py", line 426, in _make_request
six.raise_from(e, None)
File "<string>", line 3, in raise_from
# Permission is hereby granted, free of charge, to any person obtaining a copy
File "urllib3/connectionpool.py", line 421, in _make_request
httplib_response = conn.getresponse()
File "http/client.py", line 1347, in getresponse
response.begin()
File "http/client.py", line 307, in begin
version, status, reason = self._read_status()
File "http/client.py", line 268, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "socket.py", line 669, in readinto
return self._sock.recv_into(b)
File "ssl.py", line 1241, in recv_into
return self.read(nbytes, buffer)
File "ssl.py", line 1099, in read
return self._sslobj.read(len, buffer)
File "gunicorn/workers/base.py", line 201, in handle_abort
sys.exit(1)
Based on the Create Event docs and the events integration doc it seems like I should be able to pass a value_currency when creating an event. In this case I'm trying to create a "Started Checkout" event.
It seems like that parameter is missing in this file so it is left out. We track sales in several countries so it is important for us to be able to track this attribute. Is it possible to add this parameter in?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.